Repository: hannah-sten/texify-idea
Issue: Add support for chapterbib package for multiple bibliographies

### Description

The IDE throws this exception when trying to use multiple bibliographies in the same document. I have a `main.tex` file that contains:
```
\\documentclass{article}
\\usepackage[authoryear,sectionbib]{natbib}
\\usepackage{chapterbib}

\\begin{document}
    Text.
    \\include{introduction}
    \\include{discrete_shape}
\\end{document}
```
In each of the files `introduction.tex`, `discrete_shape.tex` I have
```
\\bibliographystyle{plainnat}
\\bibliography{references}
```

`\\bibliographystyle` and `references` are marked as error in each of those files (including the first one): "already used elsewhere" and "included multiple times", respectively. And every time I copy-paste the `\\bibliographystyle/\\bibliography` lines to another file in the project, the IDE throws this exception.


### Stacktrace
```
com.intellij.diagnostic.PluginException: Reported element LatexCommandsImpl(COMMANDS)[STUB]{\\bibliography} is not from the file 'LaTeX source file' the inspection 'BibtexDuplicateBibliography' (class nl.rubensten.texifyidea.inspections.bibtex.BibtexDuplicateBibliographyInspection) was invoked for. Message: 'Bibliography file is included multiple times'.
Element' containing file: LaTeX source file
Inspection invoked for file: LaTeX source file
 [Plugin: nl.rubensten.texifyidea]
	at com.intellij.ide.plugins.PluginManagerCore.createPluginException(PluginManagerCore.java:368)
	at com.intellij.diagnostic.PluginProblemReporterImpl.createPluginExceptionByClass(PluginProblemReporterImpl.java:12)
	at com.intellij.diagnostic.PluginException.createByClass(PluginException.java:68)
	at com.intellij.diagnostic.PluginException.logPluginError(PluginException.java:84)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.createHighlightsForDescriptor(LocalInspectionsPass.java:545)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.addHighlightsFromResults(LocalInspectionsPass.java:486)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.inspect(LocalInspectionsPass.java:206)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.collectInformationWithProgress(LocalInspectionsPass.java:117)
	at com.intellij.codeInsight.daemon.impl.ProgressableTextEditorHighlightingPass.doCollectInformation(ProgressableTextEditorHighlightingPass.java:84)
	at com.intellij.codeHighlighting.TextEditorHighlightingPass.collectInformation(TextEditorHighlightingPass.java:69)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$null$1(PassExecutorService.java:430)
	at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1166)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$2(PassExecutorService.java:423)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:586)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:532)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:86)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.doRun(PassExecutorService.java:422)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$run$0(PassExecutorService.java:398)
	at com.intellij.openapi.application.impl.ReadMostlyRWLock.executeByImpatientReader(ReadMostlyRWLock.java:164)
	at com.intellij.openapi.application.impl.ApplicationImpl.executeByImpatientReader(ApplicationImpl.java:218)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.run(PassExecutorService.java:396)
	at com.intellij.concurrency.JobLauncherImpl$VoidForkJoinTask$1.exec(JobLauncherImpl.java:174)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
```

Changed files:
src/nl/hannahsten/texifyidea/run/bibtex/RunBibtexListener.kt
src/nl/hannahsten/texifyidea/run/makeindex/RunMakeindexListener.kt
src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographyInspection.kt
src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographystyleInspection.kt
src/nl/hannahsten/texifyidea/inspections/latex/LatexFileNotFoundInspection.kt
src/nl/hannahsten/texifyidea/run/latex/LatexCommandLineState.kt
src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationPanel.kt
src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationSelectionDialog.kt
src/nl/hannahsten/texifyidea/util/Packages.kt
src/nl/hannahsten/texifyidea/inspections/latex/LatexDiscouragedUseOfDefInspection.kt
src/nl/hannahsten/texifyidea/run/latex/LatexRunConfiguration.kt
src/nl/hannahsten/texifyidea/run/latex/ui/LatexSettingsEditor.kt

diff --git a/src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographyInspection.kt b/src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographyInspection.kt
index 20c2c63c0..5207ae53e 100644
--- a/src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographyInspection.kt
+++ b/src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographyInspection.kt
@@ -12,11 +12,8 @@ import nl.hannahsten.texifyidea.index.LatexIncludesIndex
 import nl.hannahsten.texifyidea.insight.InsightGroup
 import nl.hannahsten.texifyidea.inspections.TexifyInspectionBase
 import nl.hannahsten.texifyidea.psi.LatexCommands
+import nl.hannahsten.texifyidea.util.*
 import nl.hannahsten.texifyidea.util.files.document
-import nl.hannahsten.texifyidea.util.includedFileNames
-import nl.hannahsten.texifyidea.util.replaceString
-import nl.hannahsten.texifyidea.util.requiredParameter
-import nl.hannahsten.texifyidea.util.trimRange
 
 /**
  * @author Sten Wessel
@@ -33,6 +30,12 @@ open class BibtexDuplicateBibliographyInspection : TexifyInspectionBase() {
     override fun getDisplayName() = "Same bibliography is included multiple times"
 
     override fun inspectFile(file: PsiFile, manager: InspectionManager, isOntheFly: Boolean): List<ProblemDescriptor> {
+
+        // Chapterbib allows multiple bibliographies
+        if (file.includedPackages().any { it == "chapterbib" }) {
+            return emptyList()
+        }
+
         val descriptors = descriptorList()
 
         // Map each bibliography file to all the commands which include it
diff --git a/src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographystyleInspection.kt b/src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographystyleInspection.kt
index 0f8c37098..55ee482e9 100644
--- a/src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographystyleInspection.kt
+++ b/src/nl/hannahsten/texifyidea/inspections/bibtex/BibtexDuplicateBibliographystyleInspection.kt
@@ -13,6 +13,7 @@ import nl.hannahsten.texifyidea.psi.LatexCommands
 import nl.hannahsten.texifyidea.util.files.commandsInFile
 import nl.hannahsten.texifyidea.util.files.commandsInFileSet
 import nl.hannahsten.texifyidea.util.findAtLeast
+import nl.hannahsten.texifyidea.util.includedPackages
 
 /**
  * @author Hannah Schellekens
@@ -29,6 +30,12 @@ open class BibtexDuplicateBibliographystyleInspection : TexifyInspectionBase() {
     override fun getDisplayName() = "Duplicate bibliography style commands"
 
     override fun inspectFile(file: PsiFile, manager: InspectionManager, isOntheFly: Boolean): MutableList<ProblemDescriptor> {
+
+        // Chapterbib allows multiple bibliographies
+        if (file.includedPackages().any { it == "chapterbib" }) {
+            return mutableListOf()
+        }
+
         val descriptors = descriptorList()
 
         // Check if a bibliography is present.
diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/LatexDiscouragedUseOfDefInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/LatexDiscouragedUseOfDefInspection.kt
index 210fd79d0..2be5192fe 100644
--- a/src/nl/hannahsten/texifyidea/inspections/latex/LatexDiscouragedUseOfDefInspection.kt
+++ b/src/nl/hannahsten/texifyidea/inspections/latex/LatexDiscouragedUseOfDefInspection.kt
@@ -49,8 +49,6 @@ open class LatexDiscouragedUseOfDefInspection : TexifyInspectionBase() {
                         NewcommandFix(),
                         NewcommandFix("\\\\renewcommand")
                 ))
-
-                // TODO: Determine new/renew based on context.
             }
         }
 
diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/LatexFileNotFoundInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/LatexFileNotFoundInspection.kt
index 8be629333..1d5d0f05e 100644
--- a/src/nl/hannahsten/texifyidea/inspections/latex/LatexFileNotFoundInspection.kt
+++ b/src/nl/hannahsten/texifyidea/inspections/latex/LatexFileNotFoundInspection.kt
@@ -198,7 +198,7 @@ open class LatexFileNotFoundInspection : TexifyInspectionBase() {
             val root = file.findRootFile().containingDirectory.virtualFile.canonicalPath ?: return
             val document = PsiDocumentManager.getInstance(project).getDocument(file) ?: return
 
-            // Display a dialog to ask for the location and name of the new file. // todo may be bib file
+            // Display a dialog to ask for the location and name of the new file.
             val newFilePath = CreateFileDialog(file.containingDirectory.virtualFile.canonicalPath, filePath.formatAsFilePath())
                     .newFileFullPath ?: return
 
diff --git a/src/nl/hannahsten/texifyidea/run/bibtex/RunBibtexListener.kt b/src/nl/hannahsten/texifyidea/run/bibtex/RunBibtexListener.kt
index 3e6b72fc0..f016ca323 100644
--- a/src/nl/hannahsten/texifyidea/run/bibtex/RunBibtexListener.kt
+++ b/src/nl/hannahsten/texifyidea/run/bibtex/RunBibtexListener.kt
@@ -15,7 +15,8 @@ import nl.hannahsten.texifyidea.run.latex.LatexRunConfiguration
 class RunBibtexListener(
         private val bibtexSettings: RunnerAndConfigurationSettings,
         private val latexConfiguration: LatexRunConfiguration,
-        private val environment: ExecutionEnvironment
+        private val environment: ExecutionEnvironment,
+        private val runLatexAfterwards: Boolean = true
 ) : ProcessListener {
 
     override fun processTerminated(event: ProcessEvent) {
@@ -26,22 +27,24 @@ class RunBibtexListener(
         // Run bibtex compiler (blocking execution)
         RunConfigurationBeforeRunProvider.doExecuteTask(environment, bibtexSettings, null)
 
-        // Mark the next latex runs to exclude bibtex compilation
-        latexConfiguration.isFirstRunConfig = false
-        try {
-            val latexSettings = RunManagerImpl.getInstanceImpl(environment.project).getSettings(latexConfiguration)
-                    ?: return
-
-            // Compile twice to fix references etc
-            // Mark the next latex run as not being the final one, to avoid for instance opening the pdf viewer too early (with possible multiple open pdfs as a result, or a second open would fail because of a write lock)
-            latexConfiguration.isLastRunConfig = false
-            RunConfigurationBeforeRunProvider.doExecuteTask(environment, latexSettings, null)
-            latexConfiguration.isLastRunConfig = true
-            RunConfigurationBeforeRunProvider.doExecuteTask(environment, latexSettings, null)
-        }
-        finally {
-            latexConfiguration.isLastRunConfig = false
-            latexConfiguration.isFirstRunConfig = true
+        if (runLatexAfterwards) {
+            // Mark the next latex runs to exclude bibtex compilation
+            latexConfiguration.isFirstRunConfig = false
+            try {
+                val latexSettings = RunManagerImpl.getInstanceImpl(environment.project).getSettings(latexConfiguration)
+                        ?: return
+
+                // Compile twice to fix references etc
+                // Mark the next latex run as not being the final one, to avoid for instance opening the pdf viewer too early (with possible multiple open pdfs as a result, or a second open would fail because of a write lock)
+                latexConfiguration.isLastRunConfig = false
+                RunConfigurationBeforeRunProvider.doExecuteTask(environment, latexSettings, null)
+                latexConfiguration.isLastRunConfig = true
+                RunConfigurationBeforeRunProvider.doExecuteTask(environment, latexSettings, null)
+            }
+            finally {
+                latexConfiguration.isLastRunConfig = false
+                latexConfiguration.isFirstRunConfig = true
+            }
         }
     }
 
diff --git a/src/nl/hannahsten/texifyidea/run/latex/LatexCommandLineState.kt b/src/nl/hannahsten/texifyidea/run/latex/LatexCommandLineState.kt
index eb88e390e..5fd9cfdec 100644
--- a/src/nl/hannahsten/texifyidea/run/latex/LatexCommandLineState.kt
+++ b/src/nl/hannahsten/texifyidea/run/latex/LatexCommandLineState.kt
@@ -56,8 +56,18 @@ open class LatexCommandLineState(environment: ExecutionEnvironment, private val
         if (!runConfig.hasBeenRun) {
             // Only at this moment we know the user really wants to run the run configuration, so only now we do the expensive check of
             // checking for bibliography commands
-            if (runConfig.bibRunConfig == null && !compiler.includesBibtex) {
+            if (runConfig.bibRunConfigs.isEmpty() && !compiler.includesBibtex) {
                 runConfig.generateBibRunConfig()
+
+                runConfig.bibRunConfigs.forEach {
+                    val bibSettings = it ?: return@forEach
+
+                    // Pass necessary latex run configurations settings to the bibtex run configuration.
+                    (bibSettings.configuration as? BibtexRunConfiguration)?.apply {
+                        // Check if the aux, out, or src folder should be used as bib working dir.
+                        this.bibWorkingDir = runConfig.getAuxilDirectory()
+                    }
+                }
             }
         }
 
@@ -84,7 +94,7 @@ open class LatexCommandLineState(environment: ExecutionEnvironment, private val
         runConfig.hasBeenRun = true
 
         // If there is no bibtex/makeindex involved and we don't need to compile twice, then this is the last compile
-        if (runConfig.bibRunConfig == null && !isMakeindexNeeded) {
+        if (runConfig.bibRunConfigs.isEmpty() && !isMakeindexNeeded) {
             if (!runConfig.compileTwice) {
                 runConfig.isLastRunConfig = true
             }
@@ -96,22 +106,22 @@ open class LatexCommandLineState(environment: ExecutionEnvironment, private val
             }
         }
 
-        runConfig.bibRunConfig?.let {
+        runConfig.bibRunConfigs.forEachIndexed { index, bibSettings ->
             if (!runConfig.isFirstRunConfig) {
-                return@let
+                return@forEachIndexed
             }
 
-            // Pass necessary latex run configurations settings to the bibtex run configuration.
-            (it.configuration as? BibtexRunConfiguration)?.apply {
-                this.mainFile = mainFile
-                // Check if the aux, out, or src folder should be used as bib working dir.
-                this.bibWorkingDir = runConfig.getAuxilDirectory()
+            if (bibSettings == null) {
+                return@forEachIndexed
             }
 
-            handler.addProcessListener(RunBibtexListener(it, runConfig, environment))
-
-            // Skip the other handlers
-            return handler
+            // Only run latex after the last one
+            if (index == runConfig.bibRunConfigs.size - 1) {
+                handler.addProcessListener(RunBibtexListener(bibSettings, runConfig, environment, true))
+            }
+            else {
+                handler.addProcessListener(RunBibtexListener(bibSettings, runConfig, environment, false))
+            }
         }
 
         // Do not schedule to open the pdf viewer when this is not the last run config in the chain
diff --git a/src/nl/hannahsten/texifyidea/run/latex/LatexRunConfiguration.kt b/src/nl/hannahsten/texifyidea/run/latex/LatexRunConfiguration.kt
index dafab6f0d..f40eac79a 100644
--- a/src/nl/hannahsten/texifyidea/run/latex/LatexRunConfiguration.kt
+++ b/src/nl/hannahsten/texifyidea/run/latex/LatexRunConfiguration.kt
@@ -22,9 +22,10 @@ import nl.hannahsten.texifyidea.run.compiler.BibliographyCompiler
 import nl.hannahsten.texifyidea.run.compiler.LatexCompiler
 import nl.hannahsten.texifyidea.run.compiler.LatexCompiler.Format
 import nl.hannahsten.texifyidea.run.latex.ui.LatexSettingsEditor
-import nl.hannahsten.texifyidea.util.LatexDistribution
-import nl.hannahsten.texifyidea.util.hasBibliography
-import nl.hannahsten.texifyidea.util.usesBiber
+import nl.hannahsten.texifyidea.util.*
+import nl.hannahsten.texifyidea.util.files.commandsInFileSet
+import nl.hannahsten.texifyidea.util.files.findRelativeFile
+import nl.hannahsten.texifyidea.util.files.referencedFiles
 import org.jdom.Element
 
 /**
@@ -91,12 +92,16 @@ class LatexRunConfiguration constructor(project: Project,
     /** Whether the pdf viewer is allowed to claim focus after compilation. */
     var allowFocusChange = true
 
-    private var bibRunConfigId = ""
-    var bibRunConfig: RunnerAndConfigurationSettings?
-        get() = RunManagerImpl.getInstanceImpl(project)
-                .getConfigurationById(bibRunConfigId)
-        set(bibRunConfig) {
-            this.bibRunConfigId = bibRunConfig?.uniqueID ?: ""
+    private var bibRunConfigIds = mutableSetOf<String>()
+    var bibRunConfigs: Set<RunnerAndConfigurationSettings?>
+        get() = bibRunConfigIds.map {
+            RunManagerImpl.getInstanceImpl(project).getConfigurationById(it)
+        }.toSet()
+        set(bibRunConfigs) {
+            bibRunConfigIds = mutableSetOf()
+            bibRunConfigs.forEach {
+                bibRunConfigIds.add(it?.uniqueID ?: "")
+            }
         }
 
     private var makeindexRunConfigId = ""
@@ -230,9 +235,10 @@ class LatexRunConfiguration constructor(project: Project,
         val hasBeenRunString = parent.getChildText(HAS_BEEN_RUN)
         this.hasBeenRun = hasBeenRunString?.toBoolean() ?: false
 
-        // Read bibliography run configuration
+        // Read bibliography run configurations, which is a list of ids
         val bibRunConfigElt = parent.getChildText(BIB_RUN_CONFIG)
-        this.bibRunConfigId = bibRunConfigElt ?: ""
+        // Assume the list is of the form [id 1,id 2]
+        this.bibRunConfigIds = bibRunConfigElt.drop(1).dropLast(1).split(", ").toMutableSet()
 
         // Read makeindex run configuration
         val makeindexRunConfigElt = parent.getChildText(MAKEINDEX_RUN_CONFIG)
@@ -312,7 +318,7 @@ class LatexRunConfiguration constructor(project: Project,
 
         // Write bibliography run configuration
         val bibRunConfigElt = Element(BIB_RUN_CONFIG)
-        bibRunConfigElt.text = bibRunConfigId
+        bibRunConfigElt.text = bibRunConfigIds.toString()
         parent.addContent(bibRunConfigElt)
 
         // Write makeindex run configuration
@@ -321,6 +327,28 @@ class LatexRunConfiguration constructor(project: Project,
         parent.addContent(makeindexRunConfigElt)
     }
 
+    /**
+     * Create a new bib run config and add it to the set.
+     */
+    private fun addBibRunConfig(defaultCompiler: BibliographyCompiler, mainFile: VirtualFile?) {
+        val runManager = RunManagerImpl.getInstanceImpl(project)
+
+        val bibSettings = runManager.createConfiguration(
+                "",
+                LatexConfigurationFactory(BibtexRunConfigurationType())
+        )
+
+        val bibtexRunConfiguration = bibSettings.configuration as BibtexRunConfiguration
+
+        bibtexRunConfiguration.compiler = defaultCompiler
+        bibtexRunConfiguration.mainFile = mainFile
+        bibtexRunConfiguration.setSuggestedName()
+
+        runManager.addConfiguration(bibSettings)
+
+        bibRunConfigs = bibRunConfigs + setOf(bibSettings)
+    }
+
     /**
      * Generate a Bibtex run configuration, after trying to guess whether the user wants to use bibtex or biber as compiler.
      */
@@ -340,22 +368,37 @@ class LatexRunConfiguration constructor(project: Project,
             }
         }
 
-        val runManager = RunManagerImpl.getInstanceImpl(project)
 
-        val bibSettings = runManager.createConfiguration(
-                "",
-                LatexConfigurationFactory(BibtexRunConfigurationType())
-        )
+        // When chapterbib is used, every chapter has its own bibliography and needs its own run config
+        val usesChapterbib = psiFile?.includedPackages()?.contains("chapterbib") == true
 
-        val bibtexRunConfiguration = bibSettings.configuration as BibtexRunConfiguration
-
-        bibtexRunConfiguration.compiler = defaultCompiler
-        bibtexRunConfiguration.mainFile = mainFile
-        bibtexRunConfiguration.setSuggestedName()
 
-        runManager.addConfiguration(bibSettings)
 
-        bibRunConfig = bibSettings
+        if (!usesChapterbib) {
+            addBibRunConfig(defaultCompiler, mainFile)
+        }
+        else if (psiFile != null) {
+            val allBibliographyCommands = psiFile!!.commandsInFileSet().filter { it.name == "\\\\bibliography" }
+
+            // We know that there can only be one bibliography per top-level \\include,
+            // however not all of them may contain a bibliography, and the ones
+            // that do have one can have it in any included file
+            psiFile!!.allCommands()
+                .filter { it.name == "\\\\include" }
+                .flatMap { command -> command.requiredParameters }
+                .forEach {filename ->
+                    // Find all the files of this chapter, then check if any of the bibliography commands appears in a file in this chapter
+                    val chapterMainFile = psiFile!!.findRelativeFile(filename) ?: return@forEach
+
+                    val chapterFiles = chapterMainFile.referencedFiles().toMutableSet().apply { add(chapterMainFile) }
+
+                    val chapterHasBibliography = allBibliographyCommands.any { it.containingFile in chapterFiles }
+
+                    if (chapterHasBibliography) {
+                        addBibRunConfig(defaultCompiler, chapterMainFile.virtualFile)
+                    }
+                }
+        }
     }
 
     /**
diff --git a/src/nl/hannahsten/texifyidea/run/latex/ui/LatexSettingsEditor.kt b/src/nl/hannahsten/texifyidea/run/latex/ui/LatexSettingsEditor.kt
index 980a24575..aa408d84a 100644
--- a/src/nl/hannahsten/texifyidea/run/latex/ui/LatexSettingsEditor.kt
+++ b/src/nl/hannahsten/texifyidea/run/latex/ui/LatexSettingsEditor.kt
@@ -129,10 +129,10 @@ class LatexSettingsEditor(private var project: Project?) : SettingsEditor<LatexR
         project = runConfiguration.project
 
         // Reset bibliography
-        bibliographyPanel.configuration = runConfiguration.bibRunConfig
+        bibliographyPanel.configurations = runConfiguration.bibRunConfigs.filterNotNull().toMutableSet()
 
         // Reset makeindex
-        makeindexPanel.configuration = runConfiguration.makeindexRunConfig
+        makeindexPanel.configurations = if (runConfiguration.makeindexRunConfig != null) mutableSetOf(runConfiguration.makeindexRunConfig!!) else mutableSetOf()
     }
 
     // Confirm the changes, i.e. copy current UI state into the target settings object.
@@ -144,14 +144,14 @@ class LatexSettingsEditor(private var project: Project?) : SettingsEditor<LatexR
 
         // Remove bibtex run config when switching to a compiler which includes running bibtex
         if (runConfiguration.compiler?.includesBibtex == true) {
-            runConfiguration.bibRunConfig = null
+            runConfiguration.bibRunConfigs = setOf()
             bibliographyPanel.isVisible = false
         }
         else {
             bibliographyPanel.isVisible = true
 
             // Apply bibliography, only if not hidden
-            runConfiguration.bibRunConfig = bibliographyPanel.configuration
+            runConfiguration.bibRunConfigs = bibliographyPanel.configurations
         }
 
         if (runConfiguration.compiler?.includesMakeindex == true) {
@@ -162,7 +162,8 @@ class LatexSettingsEditor(private var project: Project?) : SettingsEditor<LatexR
             makeindexPanel.isVisible = true
 
             // Apply makeindex
-            runConfiguration.makeindexRunConfig = makeindexPanel.configuration
+            // For now we just run the first one, this can be extended to run all of them but that requires some extra work
+            runConfiguration.makeindexRunConfig = makeindexPanel.configurations.firstOrNull()
         }
 
         // Apply custom compiler path if applicable
diff --git a/src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationPanel.kt b/src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationPanel.kt
index 19615b2d8..41f6b574a 100644
--- a/src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationPanel.kt
+++ b/src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationPanel.kt
@@ -3,13 +3,10 @@ package nl.hannahsten.texifyidea.run.latex.ui
 import com.intellij.execution.RunnerAndConfigurationSettings
 import com.intellij.execution.configurations.ConfigurationType
 import com.intellij.execution.impl.RunManagerImpl
-import com.intellij.openapi.actionSystem.AnActionEvent
 import com.intellij.openapi.project.Project
-import com.intellij.ui.AnActionButton
 import com.intellij.ui.HideableTitledPanel
 import com.intellij.ui.ToolbarDecorator
 import com.intellij.ui.components.JBList
-import com.intellij.util.IconUtil
 import java.awt.BorderLayout
 import javax.swing.DefaultListSelectionModel
 import javax.swing.JPanel
@@ -26,7 +23,7 @@ class RunConfigurationPanel<RunConfigurationType : ConfigurationType>(
     private val hidePanel: HideableTitledPanel
     private lateinit var list: JBList<RunnerAndConfigurationSettings>
 
-    var configuration: RunnerAndConfigurationSettings? = null
+    var configurations: MutableSet<RunnerAndConfigurationSettings> = mutableSetOf()
         set(value) {
             field = value
             configurationChanged()
@@ -43,49 +40,36 @@ class RunConfigurationPanel<RunConfigurationType : ConfigurationType>(
 
     private fun createPanel() {
         list = JBList<RunnerAndConfigurationSettings>().apply {
-            visibleRowCount = 1
-            emptyText.text = "No run configuration selected."
+            emptyText.text = "No run configurations selected."
             cellRenderer = RunConfigCellRenderer(project)
 
             // Cell height
             prototypeCellValue = RunManagerImpl.getInstanceImpl(project).allSettings.firstOrNull()
 
-            // Disable selection
-            selectionModel = object : DefaultListSelectionModel() {
-                override fun setSelectionInterval(index0: Int, index1: Int) {
-                    super.setSelectionInterval(-1, -1)
-                    fireValueChanged(-1, -1, false)
-                }
-            }
+            selectionModel = DefaultListSelectionModel()
         }
 
         val toolbar = ToolbarDecorator.createDecorator(list).apply {
             setAsUsualTopToolbar()
 
+            // No support for executing run configs in a certain order (yet)
             disableUpDownActions()
-            disableRemoveAction()
 
-            setAddIcon(IconUtil.getEditIcon())
             setAddAction {
-                configuration = askRunConfiguration() ?: return@setAddAction
+                configurations.addAll(askRunConfigurations())
+                configurationChanged()
             }
 
-            val removeAction = object : AnActionButton("Remove", IconUtil.getRemoveIcon()) {
-
-                override fun isEnabled() = !list.isEmpty
-
-                override fun actionPerformed(e: AnActionEvent) {
-                    configuration = null
-                }
+            setRemoveAction {
+                configurations.removeAll(list.selectedValuesList)
+                configurationChanged()
             }
-
-            addExtraAction(removeAction)
         }
 
         contentPanel.add(toolbar.createPanel(), BorderLayout.CENTER)
     }
 
-    private fun askRunConfiguration(): RunnerAndConfigurationSettings? {
+    private fun askRunConfigurations(): List<RunnerAndConfigurationSettings> {
         val configurations = RunManagerImpl.getInstanceImpl(project).allSettings.filter { it.type.javaClass == runConfigurationType  }
 
         val dialog = RunConfigurationSelectionDialog(project, configurations)
@@ -100,16 +84,19 @@ class RunConfigurationPanel<RunConfigurationType : ConfigurationType>(
     }
 
     private fun configurationChanged() {
-        if (configuration != null) {
-            list.setListData(Array(1) { configuration })
+        if (configurations.isNotEmpty()) {
+            list.setListData(configurations.map { it }.toTypedArray() )
+            // Set cell height based on the size of the content
+            list.visibleRowCount = configurations.size
 
             // Mock value change to commit changes (otherwise the apply button is not activated)
-            list.setSelectionInterval(-1, -1)
+            list.setSelectionInterval(0, 0)
             changeTitle("Enabled")
             return
         }
 
         list.setListData(emptyArray())
+        list.visibleRowCount = 1
 
         // Mock value change to commit changes (otherwise the apply button is not activated)
         list.setSelectionInterval(-1, -1)
diff --git a/src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationSelectionDialog.kt b/src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationSelectionDialog.kt
index 932edda9f..840cc222b 100644
--- a/src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationSelectionDialog.kt
+++ b/src/nl/hannahsten/texifyidea/run/latex/ui/RunConfigurationSelectionDialog.kt
@@ -20,13 +20,13 @@ import javax.swing.SwingUtilities
 class RunConfigurationSelectionDialog(
         private val project: Project,
         private val settings: List<RunnerAndConfigurationSettings>,
-        selected: RunnerAndConfigurationSettings? = null
+        selected: List<RunnerAndConfigurationSettings> = emptyList()
 ) : DialogWrapper(project) {
 
     private lateinit var list: JBList<RunnerAndConfigurationSettings>
 
     var selected = selected
-        get() = if (isOK) field else null
+        get() = if (isOK) field else emptyList()
 
     init {
         title = "Choose Run Configuration"
@@ -55,11 +55,11 @@ class RunConfigurationSelectionDialog(
 
     override fun createCenterPanel(): JComponent? {
         list = JBList(settings).apply {
-            selectionMode = ListSelectionModel.SINGLE_SELECTION
+            selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
 
             selectionModel.addListSelectionListener {
-                selected = list.selectedValue
-                isOKActionEnabled = list.selectedValue != null
+                selected = list.selectedValuesList
+                isOKActionEnabled = !list.selectedValuesList.isNullOrEmpty()
             }
 
             cellRenderer = RunConfigCellRenderer(project)
diff --git a/src/nl/hannahsten/texifyidea/run/makeindex/RunMakeindexListener.kt b/src/nl/hannahsten/texifyidea/run/makeindex/RunMakeindexListener.kt
index 1b35cdec8..85e9bc812 100644
--- a/src/nl/hannahsten/texifyidea/run/makeindex/RunMakeindexListener.kt
+++ b/src/nl/hannahsten/texifyidea/run/makeindex/RunMakeindexListener.kt
@@ -47,7 +47,7 @@ class RunMakeindexListener(
             if (!copyIndexFile(indexFilename)) return
 
             // Don't schedule more latex runs if bibtex is used, because that will already schedule the extra runs
-            if (latexRunConfig.bibRunConfig == null) {
+            if (latexRunConfig.bibRunConfigs.isEmpty()) {
                 // LaTeX twice
                 latexRunConfig.isFirstRunConfig = false
                 val latexSettings = RunManagerImpl.getInstanceImpl(environment.project).getSettings(latexRunConfig)
diff --git a/src/nl/hannahsten/texifyidea/util/Packages.kt b/src/nl/hannahsten/texifyidea/util/Packages.kt
index add480131..cd3ae42dc 100644
--- a/src/nl/hannahsten/texifyidea/util/Packages.kt
+++ b/src/nl/hannahsten/texifyidea/util/Packages.kt
@@ -32,7 +32,6 @@ object PackageUtils {
 
     /**
      * Inserts a usepackage statement for the given package in a certain file.
-     * todo all callers should use other usepackage?
      *
      * @param file
      *          The file to add the usepackage statement to.