diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/LatexPackageNotInstalledInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/LatexPackageNotInstalledInspection.kt index 22e210167..e3d327e27 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/LatexPackageNotInstalledInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/LatexPackageNotInstalledInspection.kt @@ -21,7 +21,15 @@ import nl.hannahsten.texifyidea.psi.LatexCommands import nl.hannahsten.texifyidea.run.latex.LatexDistribution import nl.hannahsten.texifyidea.util.* +/** + * Check if a LaTeX package is not installed (only for TeX Live, since MiKTeX downloads them automatically). + */ class LatexPackageNotInstalledInspection : TexifyInspectionBase() { + // This caches packages which are not installed, which is needed + // otherwise we are running the expensive call to tlmgr basically on + // every letter typed - exactly the same call with the same results + private val knownNotInstalledPackages = mutableSetOf() + override val inspectionGroup: InsightGroup = InsightGroup.LATEX override val inspectionId: String = @@ -43,7 +51,7 @@ class LatexPackageNotInstalledInspection : TexifyInspectionBase() { val customPackages = LatexDefinitionIndex.getCommandsByName("\\ProvidesPackage", file.project, file.project .projectSearchScope) .map { it.requiredParameter(0) } - .map { it?.toLowerCase() } + .mapNotNull { it?.toLowerCase() } val packages = installedPackages + customPackages val commands = file.childrenOfType(LatexCommands::class) @@ -52,16 +60,21 @@ class LatexPackageNotInstalledInspection : TexifyInspectionBase() { for (command in commands) { val `package` = command.requiredParameters.firstOrNull()?.toLowerCase() ?: continue if (`package` !in packages) { - // Manually check if the package is installed (e.g. rubikrotation is listed as rubik, so we need to check it separately). - if ("tlmgr search --file /$`package`.sty".runCommand() + // Use the cache or manually check if the package is installed (e.g. rubikrotation is listed as rubik, so we need to check it separately). + if (knownNotInstalledPackages.contains(`package`) || "tlmgr search --file /$`package`.sty".runCommand() ?.isEmpty() == true) { descriptors.add(manager.createProblemDescriptor( command, "Package is not installed or \\ProvidesPackage is missing", - InstallPackage(SmartPointerManager.getInstance(file.project).createSmartPsiElementPointer(file), `package`), + InstallPackage(SmartPointerManager.getInstance(file.project).createSmartPsiElementPointer(file), `package`, knownNotInstalledPackages), ProblemHighlightType.WARNING, isOntheFly )) + knownNotInstalledPackages.add(`package`) + } + else { + // Apparently the package is installed, but was not found initially by the TexLivePackageListInitializer (for example stackrel, contained in the oberdiek bundle) + TexLivePackages.packageList.add(`package`) } } } @@ -69,7 +82,7 @@ class LatexPackageNotInstalledInspection : TexifyInspectionBase() { return descriptors } - private class InstallPackage(val filePointer: SmartPsiElementPointer, val packageName: String) : LocalQuickFix { + private class InstallPackage(val filePointer: SmartPsiElementPointer, val packageName: String, val knownNotInstalledPackages: MutableSet) : LocalQuickFix { override fun getFamilyName(): String = "Install $packageName" /** @@ -77,6 +90,9 @@ class LatexPackageNotInstalledInspection : TexifyInspectionBase() { * packages when done. */ override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + // I don't know if you actually could install multiple packages + // with one fix, but it's not a bad idea to clear cache once in a while + knownNotInstalledPackages.clear() ProgressManager.getInstance() .run(object : Task.Backgroundable(project, "Installing $packageName...") { override fun run(indicator: ProgressIndicator) { diff --git a/src/nl/hannahsten/texifyidea/util/Packages.kt b/src/nl/hannahsten/texifyidea/util/Packages.kt index 8362f583a..12c098927 100644 --- a/src/nl/hannahsten/texifyidea/util/Packages.kt +++ b/src/nl/hannahsten/texifyidea/util/Packages.kt @@ -294,6 +294,9 @@ object PackageUtils { } object TexLivePackages { + /** + * List of installed packages. + */ var packageList: MutableList = mutableListOf() /** @@ -314,6 +317,7 @@ object TexLivePackages { fun findTexLiveName(task: Task.Backgroundable, packageName: String): String? { // Find the package name for tlmgr. task.title = "Searching for $packageName..." + // Assume that you can not use the bundle name in a \usepackage if it is different from the package name (otherwise this search won't work and we would need to use tlmgr search --global $packageName val searchResult = "tlmgr search --file --global /$packageName.sty".runCommand() ?: return null diff --git a/test/nl/hannahsten/texifyidea/reference/LatexEnvironmentReferenceTest.kt b/test/nl/hannahsten/texifyidea/reference/LatexEnvironmentReferenceTest.kt index eb3aaa2f8..4ef9e86e9 100644 --- a/test/nl/hannahsten/texifyidea/reference/LatexEnvironmentReferenceTest.kt +++ b/test/nl/hannahsten/texifyidea/reference/LatexEnvironmentReferenceTest.kt @@ -16,4 +16,16 @@ class LatexEnvironmentReferenceTest : BasePlatformTestCase() { \end{nodocument} """.trimIndent()) } + + fun testEnvironmentRenameBegin() { + myFixture.configureByText(LatexFileType, """ + \begin{document} + \end{document} + """.trimIndent()) + myFixture.renameElementAtCaret("nodocument") + myFixture.checkResult(""" + \begin{nodocument} + \end{nodocument} + """.trimIndent()) + } } \ No newline at end of file