From 6d7b1c29cc47d0037375059cc037f8d9b8d8aad0 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Fri, 27 Dec 2024 14:38:38 +0100 Subject: [PATCH 1/9] Clean up LatexPackageNotInstalledInspection --- resources/META-INF/extensions/startup.xml | 1 - .../LatexPackageNotInstalledInspection.kt | 97 +++++++++++-------- .../startup/TexLivePackageListInitializer.kt | 18 ---- 3 files changed, 56 insertions(+), 60 deletions(-) delete mode 100644 src/nl/hannahsten/texifyidea/startup/TexLivePackageListInitializer.kt diff --git a/resources/META-INF/extensions/startup.xml b/resources/META-INF/extensions/startup.xml index 87eb37d101..d212349829 100644 --- a/resources/META-INF/extensions/startup.xml +++ b/resources/META-INF/extensions/startup.xml @@ -1,7 +1,6 @@ - \ No newline at end of file diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageNotInstalledInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageNotInstalledInspection.kt index be2869d0d4..9f9902f04c 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageNotInstalledInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageNotInstalledInspection.kt @@ -1,6 +1,7 @@ package nl.hannahsten.texifyidea.inspections.latex.probablebugs.packages import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer +import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor @@ -17,10 +18,13 @@ import com.intellij.psi.SmartPsiElementPointer import nl.hannahsten.texifyidea.index.LatexDefinitionIndex import nl.hannahsten.texifyidea.inspections.InsightGroup import nl.hannahsten.texifyidea.inspections.TexifyInspectionBase +import nl.hannahsten.texifyidea.lang.commands.LatexGenericRegularCommand import nl.hannahsten.texifyidea.psi.LatexCommands import nl.hannahsten.texifyidea.reference.InputFileReference import nl.hannahsten.texifyidea.settings.sdk.LatexSdkUtil +import nl.hannahsten.texifyidea.settings.sdk.TexliveSdk import nl.hannahsten.texifyidea.util.TexLivePackages +import nl.hannahsten.texifyidea.util.magic.cmd import nl.hannahsten.texifyidea.util.parser.childrenOfType import nl.hannahsten.texifyidea.util.parser.requiredParameter import nl.hannahsten.texifyidea.util.projectSearchScope @@ -49,48 +53,54 @@ class LatexPackageNotInstalledInspection : TexifyInspectionBase() { override fun inspectFile(file: PsiFile, manager: InspectionManager, isOntheFly: Boolean): List { val descriptors = descriptorList() // We have to check whether tlmgr is installed, for those users who don't want to install TeX Live in the official way - if (LatexSdkUtil.isTlmgrAvailable(file.project)) { - val installedPackages = TexLivePackages.packageList - val customPackages = LatexDefinitionIndex.Util.getCommandsByName( - "\\ProvidesPackage", file.project, - file.project - .projectSearchScope - ) - .map { it.requiredParameter(0) } - .mapNotNull { it?.lowercase(Locale.getDefault()) } - val packages = installedPackages + customPackages - - val commands = file.childrenOfType(LatexCommands::class) - .filter { it.name == "\\usepackage" || it.name == "\\RequirePackage" } - - for (command in commands) { - @Suppress("ktlint:standard:property-naming") - val `package` = command.getRequiredParameters().firstOrNull()?.lowercase(Locale.getDefault()) ?: continue - if (`package` !in packages) { - // Use the cache or check if the file reference resolves (in the same way we resolve for the gutter icon). - if ( - knownNotInstalledPackages.contains(`package`) || - command.references.filterIsInstance().mapNotNull { it.resolve() }.isEmpty() - ) { - descriptors.add( - manager.createProblemDescriptor( - command, - "Package is not installed or \\ProvidesPackage is missing", - InstallPackage( - SmartPointerManager.getInstance(file.project).createSmartPsiElementPointer(file), - `package`, - knownNotInstalledPackages - ), - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - isOntheFly - ) + if (!LatexSdkUtil.isTlmgrAvailable(file.project)) return descriptors + + if (TexLivePackages.packageList.isEmpty() && TexliveSdk.Cache.isAvailable) { + val result = "tlmgr list --only-installed".runCommand() ?: return emptyList() + TexLivePackages.packageList = Regex("i\\s(.*):").findAll(result) + .map { it.groupValues.last() }.toMutableList() + } + + val installedPackages = TexLivePackages.packageList + val customPackages = LatexDefinitionIndex.Util.getCommandsByName( + LatexGenericRegularCommand.PROVIDESPACKAGE.cmd, file.project, + file.project + .projectSearchScope + ) + .map { it.requiredParameter(0) } + .mapNotNull { it?.lowercase(Locale.getDefault()) } + val packages = installedPackages + customPackages + + val commands = file.childrenOfType(LatexCommands::class) + .filter { it.name == LatexGenericRegularCommand.USEPACKAGE.cmd || it.name == LatexGenericRegularCommand.REQUIREPACKAGE.cmd } + + for (command in commands) { + @Suppress("ktlint:standard:property-naming") + val `package` = command.getRequiredParameters().firstOrNull()?.lowercase(Locale.getDefault()) ?: continue + if (`package` !in packages) { + // Use the cache or check if the file reference resolves (in the same way we resolve for the gutter icon). + if ( + knownNotInstalledPackages.contains(`package`) || + command.references.filterIsInstance().mapNotNull { it.resolve() }.isEmpty() + ) { + descriptors.add( + manager.createProblemDescriptor( + command, + "Package is not installed or \\ProvidesPackage is missing", + InstallPackage( + SmartPointerManager.getInstance(file.project).createSmartPsiElementPointer(file), + `package`, + knownNotInstalledPackages + ), + ProblemHighlightType.GENERIC_ERROR_OR_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`) - } + ) + 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`) } } } @@ -101,6 +111,11 @@ class LatexPackageNotInstalledInspection : TexifyInspectionBase() { override fun getFamilyName(): String = "Install $packageName" + override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { + // Nothing is modified + return IntentionPreviewInfo.EMPTY + } + /** * Install the package in the background and add it to the list of installed * packages when done. diff --git a/src/nl/hannahsten/texifyidea/startup/TexLivePackageListInitializer.kt b/src/nl/hannahsten/texifyidea/startup/TexLivePackageListInitializer.kt deleted file mode 100644 index b23a97a24b..0000000000 --- a/src/nl/hannahsten/texifyidea/startup/TexLivePackageListInitializer.kt +++ /dev/null @@ -1,18 +0,0 @@ -package nl.hannahsten.texifyidea.startup - -import com.intellij.openapi.project.Project -import com.intellij.openapi.startup.ProjectActivity -import nl.hannahsten.texifyidea.settings.sdk.TexliveSdk -import nl.hannahsten.texifyidea.util.TexLivePackages -import nl.hannahsten.texifyidea.util.runCommandNonBlocking - -class TexLivePackageListInitializer : ProjectActivity { - - override suspend fun execute(project: Project) { - if (TexliveSdk.Cache.isAvailable) { - val result = "tlmgr list --only-installed".runCommandNonBlocking().output ?: return - TexLivePackages.packageList = Regex("i\\s(.*):").findAll(result) - .map { it.groupValues.last() }.toMutableList() - } - } -} \ No newline at end of file From 0cfbaa2a8581d9f20bbe217e2e9d53adb98f754d Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Fri, 27 Dec 2024 20:54:49 +0100 Subject: [PATCH 2/9] Add LatexPackageUpdateInspection --- CHANGELOG.md | 1 + Writerside/topics/Packages.md | 7 + .../latex/probablebugs/packages.xml | 4 + .../LatexPackageUpdate.html | 8 ++ .../packages/LatexPackageUpdateInspection.kt | 130 ++++++++++++++++++ .../LatexPackageUpdateInspectionTest.kt | 28 ++++ 6 files changed, 178 insertions(+) create mode 100644 resources/inspectionDescriptions/LatexPackageUpdate.html create mode 100644 src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt create mode 100644 test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspectionTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 105ed6434f..ba114a7003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added * Add option to disable automatic compilation in power save mode * Convert automatic compilation settings to a combobox +* Add inspection to check for LaTeX package updates * Add checkboxes to graphic insertion wizard for relative width or height ### Fixed diff --git a/Writerside/topics/Packages.md b/Writerside/topics/Packages.md index b6ddc6e029..d5eaf019a8 100644 --- a/Writerside/topics/Packages.md +++ b/Writerside/topics/Packages.md @@ -13,6 +13,13 @@ This inspection is for TeX Live only, MiKTeX automatically installs packages on When using `\usepackage` or `\RequirePackage`, TeXiFy checks if the packages is installed. If it isn’t installed, it provides a quick fix to install the package. +## Package update available +_Since b0.9.10_ + +When a package has an update available on CTAN, this inspection will provide a quickfix to update the package. +Currently, it only works when tlmgr (TeX Live manager) is installed. +The list of available package updates is cached until IntelliJ is restarted or the quickfix is used. + ## Package name does not match file name _Since b0.6.10_ diff --git a/resources/META-INF/extensions/inspections/latex/probablebugs/packages.xml b/resources/META-INF/extensions/inspections/latex/probablebugs/packages.xml index 9f46f9a2b2..db654fe87a 100644 --- a/resources/META-INF/extensions/inspections/latex/probablebugs/packages.xml +++ b/resources/META-INF/extensions/inspections/latex/probablebugs/packages.xml @@ -8,6 +8,10 @@ groupPath="LaTeX" groupName="Probable bugs" displayName="Package is not installed" enabledByDefault="true" level="WARNING" /> + + +The package given in a \usepackage or \RequirePackage has an update available. + +This inspection only checks installed packages on texlive systems (with tlmgr). + + \ No newline at end of file diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt new file mode 100644 index 0000000000..6ca69c15c4 --- /dev/null +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt @@ -0,0 +1,130 @@ +package nl.hannahsten.texifyidea.inspections.latex.probablebugs.packages + +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer +import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.notification.Notification +import com.intellij.notification.NotificationType +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.progress.Task.Backgroundable +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import com.intellij.psi.SmartPointerManager +import com.intellij.psi.SmartPsiElementPointer +import nl.hannahsten.texifyidea.inspections.InsightGroup +import nl.hannahsten.texifyidea.inspections.TexifyInspectionBase +import nl.hannahsten.texifyidea.psi.LatexCommands +import nl.hannahsten.texifyidea.settings.sdk.LatexSdkUtil +import nl.hannahsten.texifyidea.settings.sdk.TexliveSdk +import nl.hannahsten.texifyidea.util.magic.CommandMagic +import nl.hannahsten.texifyidea.util.parser.childrenOfType +import nl.hannahsten.texifyidea.util.parser.requiredParameter +import nl.hannahsten.texifyidea.util.runCommand +import nl.hannahsten.texifyidea.util.runCommandWithExitCode + +/** + * Check for available updates for LaTeX packages. + * Also see [LatexPackageNotInstalledInspection]. + */ +class LatexPackageUpdateInspection : TexifyInspectionBase() { + + object Cache { + /** Map package name to old and new revision number */ + var availablePackageUpdates = mapOf>() + } + + override val inspectionGroup = InsightGroup.LATEX + + override val inspectionId = "PackageUpdate" + + override fun getDisplayName() = "Package has an update available" + + override fun inspectFile(file: PsiFile, manager: InspectionManager, isOntheFly: Boolean): List { + if (!LatexSdkUtil.isTlmgrAvailable(file.project) || !TexliveSdk.Cache.isAvailable) return emptyList() + + if (Cache.availablePackageUpdates.isEmpty()) { + val tlmgrExecutable = LatexSdkUtil.getExecutableName("tlmgr", file.project) + val result = runCommand(tlmgrExecutable, "update", "--list") ?: return emptyList() + Cache.availablePackageUpdates = """update:\s*(?[^ ]+).*local:\s*(?\d+), source:\s*(?\d+)""".toRegex() + .findAll(result) + .mapNotNull { Pair(it.groups["package"]?.value ?: return@mapNotNull null, Pair(it.groups["local"]?.value, it.groups["source"]?.value)) } + .associate { it } + } + + return file.childrenOfType() + .filter { it.name in CommandMagic.packageInclusionCommands } + .filter { it.requiredParameter(0) in Cache.availablePackageUpdates.keys } + .mapNotNull { + val packageName = it.requiredParameter(0) ?: return@mapNotNull null + val packageVersions = Cache.availablePackageUpdates[packageName] ?: return@mapNotNull null + manager.createProblemDescriptor( + it, + "Update available for package $packageName", + UpdatePackage(SmartPointerManager.getInstance(file.project).createSmartPsiElementPointer(file), packageName, packageVersions.first, packageVersions.second), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + isOntheFly + ) + } + } + + private class UpdatePackage(val filePointer: SmartPsiElementPointer, val packageName: String, val old: String?, val new: String?) : LocalQuickFix { + + override fun getFamilyName(): String = if (old != null && new != null) "Update $packageName from revision $old to revision $new" else "Update $packageName" + + override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { + // Nothing is modified + return IntentionPreviewInfo.EMPTY + } + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + ProgressManager.getInstance().run(object : Backgroundable(project, "Updating $packageName...") { + override fun run(indicator: ProgressIndicator) { + val tlmgrExecutable = LatexSdkUtil.getExecutableName("tlmgr", project) + + var (output, exitCode) = runCommandWithExitCode(tlmgrExecutable, "update", packageName, returnExceptionMessage = true, timeout = 10) + if (output?.contains("tlmgr update --self") == true) { + val (tlmgrOutput, tlmgrExitCode) = runCommandWithExitCode(tlmgrExecutable, "update", "--self", returnExceptionMessage = true, timeout = 10) + if (tlmgrExitCode != 0) { + Notification( + "LaTeX", + "Package $packageName not updated", + "Could not update tlmgr: $tlmgrOutput", + NotificationType.ERROR + ).notify(project) + indicator.cancel() + } + title = "Updating $packageName..." + val (secondOutput, secondExitCode) = runCommandWithExitCode(tlmgrExecutable, "update", packageName, returnExceptionMessage = true, timeout = 10) + output = secondOutput + exitCode = secondExitCode + } + + if (exitCode != 0) { + Notification( + "LaTeX", + "Package $packageName not updated", + "Could not update $packageName: $output", + NotificationType.ERROR + ).notify(project) + indicator.cancel() + } + } + + override fun onSuccess() { + // Clear cache, since we changed something + Cache.availablePackageUpdates = mapOf() + // Rerun inspections + DaemonCodeAnalyzer.getInstance(project) + .restart( + filePointer.containingFile + ?: return + ) + } + }) + } + } +} \ No newline at end of file diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspectionTest.kt new file mode 100644 index 0000000000..fcf7016cdb --- /dev/null +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspectionTest.kt @@ -0,0 +1,28 @@ +package nl.hannahsten.texifyidea.inspections.latex.probablebugs.packages + +import io.mockk.every +import io.mockk.mockkObject +import nl.hannahsten.texifyidea.inspections.TexifyInspectionTestBase +import nl.hannahsten.texifyidea.settings.sdk.LatexSdkUtil +import nl.hannahsten.texifyidea.settings.sdk.TexliveSdk +import nl.hannahsten.texifyidea.util.TexLivePackages + +class LatexPackageUpdateInspectionTest : TexifyInspectionTestBase(LatexPackageUpdateInspection()) { + + fun testWarning() { + texliveWithTlmgr() + + mockkObject(TexLivePackages) + LatexPackageUpdateInspection.Cache.availablePackageUpdates = mapOf(Pair("amsmath", Pair("71408", "72779"))) + + testHighlighting("\\usepackage{amsmath}") + } + + private fun texliveWithTlmgr(texlive: Boolean = true, tlmgr: Boolean = true) { + mockkObject(TexliveSdk.Cache) + every { TexliveSdk.Cache.isAvailable } returns texlive + + mockkObject(LatexSdkUtil) + every { LatexSdkUtil.isTlmgrInstalled } returns tlmgr + } +} \ No newline at end of file From fe4b3aa44b83015a533c1ed03d71f5c3e5921387 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sat, 28 Dec 2024 13:44:18 +0100 Subject: [PATCH 3/9] Add experimental support for the addtoluatexpath package --- CHANGELOG.md | 1 + .../file/LatexIndexableSetContributor.kt | 11 ++++-- .../texifyidea/lang/LatexPackage.kt | 1 + .../commands/LatexGenericRegularCommand.kt | 1 + .../reference/InputFileReference.kt | 8 +++++ .../texifyidea/util/files/FileSet.kt | 36 +++++++++++++++++++ 6 files changed, 56 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 866e8dd622..254e55d437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Support label references to user defined listings environment * Add option to disable automatic compilation in power save mode * Convert automatic compilation settings to a combobox +* Add experimental support for the addtoluatexpath package * Add checkboxes to graphic insertion wizard for relative width or height ### Fixed diff --git a/src/nl/hannahsten/texifyidea/index/file/LatexIndexableSetContributor.kt b/src/nl/hannahsten/texifyidea/index/file/LatexIndexableSetContributor.kt index 70aef3cd79..56be336e54 100644 --- a/src/nl/hannahsten/texifyidea/index/file/LatexIndexableSetContributor.kt +++ b/src/nl/hannahsten/texifyidea/index/file/LatexIndexableSetContributor.kt @@ -14,6 +14,7 @@ import nl.hannahsten.texifyidea.index.LatexIncludesIndex import nl.hannahsten.texifyidea.settings.TexifySettings import nl.hannahsten.texifyidea.settings.sdk.LatexSdkUtil import nl.hannahsten.texifyidea.util.Log +import nl.hannahsten.texifyidea.util.files.addToLuatexPathSearchDirectories import nl.hannahsten.texifyidea.util.getTexinputsPaths import nl.hannahsten.texifyidea.util.isTestProject import nl.hannahsten.texifyidea.util.magic.CommandMagic @@ -76,8 +77,8 @@ class LatexIndexableSetContributor : IndexableSetContributor() { // Using the index while building it may be problematic, cache the result and hope it doesn't create too much trouble if (Cache.externalDirectFileInclusions == null && !DumbService.isDumb(project)) { - runInBackground(project, "Searching for external bib files...") { - // For now, just do this for bibliography and direct input commands, as there this is most common + runInBackground(project, "Searching for inclusions by absolute path...") { + // Bibliography and direct input commands val commandNames = CommandMagic.includeOnlyExtensions.entries.filter { it.value.contains("bib") || it.value.contains("tex") }.map { it.key }.toSet() val externalFiles = runReadAction { LatexIncludesIndex.Util.getCommandsByNames(commandNames, project, GlobalSearchScope.projectScope(project)) @@ -93,6 +94,12 @@ class LatexIndexableSetContributor : IndexableSetContributor() { } runReadAction { file?.parent } } + .toMutableList() + + // addtoluatexpath package + val luatexPathDirectories = addToLuatexPathSearchDirectories(project) + externalFiles.addAll(luatexPathDirectories) + Cache.externalDirectFileInclusions = externalFiles.toSet() } } diff --git a/src/nl/hannahsten/texifyidea/lang/LatexPackage.kt b/src/nl/hannahsten/texifyidea/lang/LatexPackage.kt index bc42d74818..9bd6f315cc 100644 --- a/src/nl/hannahsten/texifyidea/lang/LatexPackage.kt +++ b/src/nl/hannahsten/texifyidea/lang/LatexPackage.kt @@ -27,6 +27,7 @@ open class LatexPackage @JvmOverloads constructor( // Predefined packages. val DEFAULT = LatexPackage("") + val ADDTOLUATEXPATH = LatexPackage("addtoluatexpath") val ALGORITHM2E = LatexPackage("algorithm2e") val ALGORITHMICX = LatexPackage("algorithmicx") val ALGPSEUDOCODE = LatexPackage("algpseudocode") diff --git a/src/nl/hannahsten/texifyidea/lang/commands/LatexGenericRegularCommand.kt b/src/nl/hannahsten/texifyidea/lang/commands/LatexGenericRegularCommand.kt index ae9658d81a..559a0d3f18 100644 --- a/src/nl/hannahsten/texifyidea/lang/commands/LatexGenericRegularCommand.kt +++ b/src/nl/hannahsten/texifyidea/lang/commands/LatexGenericRegularCommand.kt @@ -32,6 +32,7 @@ enum class LatexGenericRegularCommand( ) : LatexCommand { ADDTOCOUNTER("addtocounter", "countername".asRequired(), "value".asRequired()), + ADDTOLUATEXPATH("addtoluatexpath", RequiredFolderArgument("paths")), A_RING("aa", display = "å"), CAPITAL_A_RING("AA", display = "Å"), ADDBIBRESOURCE("addbibresource", RequiredFileArgument("bibliographyfile", true, false, "bib"), dependency = BIBLATEX), diff --git a/src/nl/hannahsten/texifyidea/reference/InputFileReference.kt b/src/nl/hannahsten/texifyidea/reference/InputFileReference.kt index 55d1b802d3..529f0a4717 100644 --- a/src/nl/hannahsten/texifyidea/reference/InputFileReference.kt +++ b/src/nl/hannahsten/texifyidea/reference/InputFileReference.kt @@ -202,6 +202,14 @@ class InputFileReference( targetFile = findAnywhereInProject(processedKey) } + // addtoluatexpath package + if (targetFile == null) { + for (path in addToLuatexPathSearchDirectories(element.project)) { + targetFile = path.findFile(processedKey, extensions, supportsAnyExtension) + if (targetFile != null) break + } + } + if (targetFile == null) return null // Return a reference to the target file. diff --git a/src/nl/hannahsten/texifyidea/util/files/FileSet.kt b/src/nl/hannahsten/texifyidea/util/files/FileSet.kt index 23a97dc986..0260bfd028 100644 --- a/src/nl/hannahsten/texifyidea/util/files/FileSet.kt +++ b/src/nl/hannahsten/texifyidea/util/files/FileSet.kt @@ -1,13 +1,22 @@ package nl.hannahsten.texifyidea.util.files import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiFile +import com.intellij.psi.search.GlobalSearchScope import nl.hannahsten.texifyidea.index.BibtexEntryIndex import nl.hannahsten.texifyidea.index.LatexCommandsIndex import nl.hannahsten.texifyidea.index.LatexDefinitionIndex import nl.hannahsten.texifyidea.index.LatexIncludesIndex +import nl.hannahsten.texifyidea.lang.LatexPackage +import nl.hannahsten.texifyidea.lang.commands.LatexGenericRegularCommand import nl.hannahsten.texifyidea.psi.LatexCommands +import nl.hannahsten.texifyidea.util.magic.CommandMagic +import nl.hannahsten.texifyidea.util.magic.cmd import nl.hannahsten.texifyidea.util.parser.isDefinition +import nl.hannahsten.texifyidea.util.parser.requiredParameter /** * Finds all the files in the project that are somehow related using includes. @@ -95,3 +104,30 @@ fun PsiFile.definitionsInFileSet(): Collection { fun PsiFile.definitionsAndRedefinitionsInFileSet(): Collection { return LatexDefinitionIndex.Util.getItemsInFileSet(this) } + +/** + * The addtoluatexpath package supports adding to \input@path in different ways + */ +fun addToLuatexPathSearchDirectories(project: Project): List { + val direct = runReadAction { LatexCommandsIndex.Util.getCommandsByNames(setOf(LatexGenericRegularCommand.ADDTOLUATEXPATH.cmd), project, GlobalSearchScope.projectScope(project)) } + .mapNotNull { command -> runReadAction { command.requiredParameter(0) } } + .flatMap { it.split(",") } + val viaUsepackage = runReadAction { LatexIncludesIndex.Util.getCommandsByNames(CommandMagic.packageInclusionCommands, project, GlobalSearchScope.projectScope(project)) } + .filter { runReadAction { it.requiredParameter(0) } == LatexPackage.ADDTOLUATEXPATH.name } + .flatMap { runReadAction { it.getOptionalParameterMap().keys } } + .flatMap { it.text.split(",") } + + val luatexPathDirectories = (direct + viaUsepackage).flatMap { + val basePath = LocalFileSystem.getInstance().findFileByPath(it.trimEnd('/', '*')) ?: return@flatMap emptyList() + if (it.endsWith("/**")) { + basePath.allChildDirectories() + } + else if (it.endsWith("/*")) { + basePath.children.filter { it.isDirectory } + } + else { + listOf(basePath) + } + } + return luatexPathDirectories +} From ed9549cb7fd83395f8969fe9ae59c8667bd7a967 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sat, 28 Dec 2024 14:08:09 +0100 Subject: [PATCH 4/9] Add quickfix to update all packages --- .../packages/LatexPackageUpdateInspection.kt | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt index 6ca69c15c4..80010f2d1e 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt @@ -64,30 +64,36 @@ class LatexPackageUpdateInspection : TexifyInspectionBase() { manager.createProblemDescriptor( it, "Update available for package $packageName", - UpdatePackage(SmartPointerManager.getInstance(file.project).createSmartPsiElementPointer(file), packageName, packageVersions.first, packageVersions.second), + arrayOf( + UpdatePackage(SmartPointerManager.getInstance(file.project).createSmartPsiElementPointer(file), packageName, packageVersions.first, packageVersions.second), + UpdatePackage(SmartPointerManager.getInstance(file.project).createSmartPsiElementPointer(file), "--all", null, null), + ), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - isOntheFly + isOntheFly, + false, ) } } private class UpdatePackage(val filePointer: SmartPsiElementPointer, val packageName: String, val old: String?, val new: String?) : LocalQuickFix { - override fun getFamilyName(): String = if (old != null && new != null) "Update $packageName from revision $old to revision $new" else "Update $packageName" + override fun getFamilyName(): String = if (packageName == "--all") "Update all packages" else if (old != null && new != null) "Update $packageName from revision $old to revision $new" else "Update $packageName" override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { // Nothing is modified - return IntentionPreviewInfo.EMPTY + return IntentionPreviewInfo.Html("Run tlngr update $packageName") } override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - ProgressManager.getInstance().run(object : Backgroundable(project, "Updating $packageName...") { + val message = if (packageName == "--all") "Updating all packages" else "Updating $packageName..." + ProgressManager.getInstance().run(object : Backgroundable(project, message) { override fun run(indicator: ProgressIndicator) { val tlmgrExecutable = LatexSdkUtil.getExecutableName("tlmgr", project) - var (output, exitCode) = runCommandWithExitCode(tlmgrExecutable, "update", packageName, returnExceptionMessage = true, timeout = 10) + val timeout: Long = if (packageName == "--all") 600 else 15 + var (output, exitCode) = runCommandWithExitCode(tlmgrExecutable, "update", packageName, returnExceptionMessage = true, timeout = timeout) if (output?.contains("tlmgr update --self") == true) { - val (tlmgrOutput, tlmgrExitCode) = runCommandWithExitCode(tlmgrExecutable, "update", "--self", returnExceptionMessage = true, timeout = 10) + val (tlmgrOutput, tlmgrExitCode) = runCommandWithExitCode(tlmgrExecutable, "update", "--self", returnExceptionMessage = true, timeout = 20) if (tlmgrExitCode != 0) { Notification( "LaTeX", @@ -97,8 +103,8 @@ class LatexPackageUpdateInspection : TexifyInspectionBase() { ).notify(project) indicator.cancel() } - title = "Updating $packageName..." - val (secondOutput, secondExitCode) = runCommandWithExitCode(tlmgrExecutable, "update", packageName, returnExceptionMessage = true, timeout = 10) + title = message + val (secondOutput, secondExitCode) = runCommandWithExitCode(tlmgrExecutable, "update", packageName, returnExceptionMessage = true, timeout = timeout) output = secondOutput exitCode = secondExitCode } @@ -106,8 +112,8 @@ class LatexPackageUpdateInspection : TexifyInspectionBase() { if (exitCode != 0) { Notification( "LaTeX", - "Package $packageName not updated", - "Could not update $packageName: $output", + if (packageName == "--all") "Could not update packages" else "Package $packageName not updated", + "Could not update $packageName${if (exitCode == 143) " due to a timeout" else ""}: $output", NotificationType.ERROR ).notify(project) indicator.cancel() From 96e5aed16296ee3de3de8f760f478acfc618a471 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sat, 28 Dec 2024 14:27:20 +0100 Subject: [PATCH 5/9] Update timout --- .../latex/probablebugs/packages/LatexPackageUpdateInspection.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt index 80010f2d1e..10bfc761d3 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/packages/LatexPackageUpdateInspection.kt @@ -90,7 +90,7 @@ class LatexPackageUpdateInspection : TexifyInspectionBase() { override fun run(indicator: ProgressIndicator) { val tlmgrExecutable = LatexSdkUtil.getExecutableName("tlmgr", project) - val timeout: Long = if (packageName == "--all") 600 else 15 + val timeout: Long = if (packageName == "--all") 1200 else 15 var (output, exitCode) = runCommandWithExitCode(tlmgrExecutable, "update", packageName, returnExceptionMessage = true, timeout = timeout) if (output?.contains("tlmgr update --self") == true) { val (tlmgrOutput, tlmgrExitCode) = runCommandWithExitCode(tlmgrExecutable, "update", "--self", returnExceptionMessage = true, timeout = 20) From 25c8218b79c368b55f99fa5437b83b26126ca949 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sat, 28 Dec 2024 21:09:27 +0100 Subject: [PATCH 6/9] Fix #3813 invalid element --- src/nl/hannahsten/texifyidea/util/parser/Psi.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nl/hannahsten/texifyidea/util/parser/Psi.kt b/src/nl/hannahsten/texifyidea/util/parser/Psi.kt index 1b3b66806b..0ee9a3becf 100644 --- a/src/nl/hannahsten/texifyidea/util/parser/Psi.kt +++ b/src/nl/hannahsten/texifyidea/util/parser/Psi.kt @@ -32,7 +32,7 @@ fun PsiElement.lineNumber(): Int? = containingFile.document()?.getLineNumber(tex */ fun PsiElement.childrenOfType(clazz: KClass): Collection { return runReadAction { - if (project.isDisposed || !this.isValid) { + if (!this.isValid || project.isDisposed) { emptyList() } else { From e1d372619ecec9877d4335f9fcc1e60e05b78870 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sat, 28 Dec 2024 21:14:17 +0100 Subject: [PATCH 7/9] Don't try to start a broken connection to Evince, fix #3818 --- .../run/linuxpdfviewer/evince/EvinceInverseSearchListener.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nl/hannahsten/texifyidea/run/linuxpdfviewer/evince/EvinceInverseSearchListener.kt b/src/nl/hannahsten/texifyidea/run/linuxpdfviewer/evince/EvinceInverseSearchListener.kt index 7b812507da..f93dfca726 100644 --- a/src/nl/hannahsten/texifyidea/run/linuxpdfviewer/evince/EvinceInverseSearchListener.kt +++ b/src/nl/hannahsten/texifyidea/run/linuxpdfviewer/evince/EvinceInverseSearchListener.kt @@ -46,6 +46,7 @@ object EvinceInverseSearchListener { } catch (e: Exception) { Notification("LaTeX", "Cannot get connection to DBus", "Check if the correct packages are installed", NotificationType.ERROR).notify(project) + return } } From aee35728443820732378ccf758db05dad7981039 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sat, 28 Dec 2024 21:18:26 +0100 Subject: [PATCH 8/9] Ignore \nameref in LatexNonBreakingSpaceInspection, fix #3793 --- CHANGELOG.md | 3 ++- .../typesetting/spacing/LatexNonBreakingSpaceInspection.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72841345dc..200a420e0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ * Fix parse error when using commands with arguments in parameter of \href or \url * Fix parse error when using parentheses in a group in a key value command argument * Fix parse erron when using inline math in cases* environment in inline math -* +* Fix exceptions #3813, #3818 +* Fix false positive non-breaking space warning for \nameref ## [0.9.10-alpha.4] - 2024-12-21 diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/typesetting/spacing/LatexNonBreakingSpaceInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/typesetting/spacing/LatexNonBreakingSpaceInspection.kt index 96a0b9dbae..5a910dffbd 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/typesetting/spacing/LatexNonBreakingSpaceInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/typesetting/spacing/LatexNonBreakingSpaceInspection.kt @@ -46,6 +46,7 @@ open class LatexNonBreakingSpaceInspection : TexifyInspectionBase() { "\\Cref", "\\cpageref", "\\autoref", + "\\nameref", "\\citeauthor", "\\textcite", "\\Textcite" From 0e2cfe4083160b7642fabb1d5717e18c54475f04 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sat, 28 Dec 2024 21:34:11 +0100 Subject: [PATCH 9/9] Add \micro to autocompletion, fix #3795 --- CHANGELOG.md | 1 + .../latex/probablebugs/LatexUnicodeInspection.kt | 2 +- .../texifyidea/lang/commands/LatexSiunitxCommand.kt | 1 + .../latex/probablebugs/LatexUnicodeInspectionTest.kt | 7 +++++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 200a420e0c..ad9670479d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * Fix parse erron when using inline math in cases* environment in inline math * Fix exceptions #3813, #3818 * Fix false positive non-breaking space warning for \nameref +* Fix confusion with \micro and \mu unicode characters ## [0.9.10-alpha.4] - 2024-12-21 diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexUnicodeInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexUnicodeInspection.kt index c99bc671d2..394321b7be 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexUnicodeInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexUnicodeInspection.kt @@ -235,7 +235,7 @@ class LatexUnicodeInspection : TexifyInspectionBase() { // Try to find in lookup for special command val replacement: String? val command: LatexCommand? = if (inMathMode) { - LatexMathCommand.findByDisplay(c)?.firstOrNull() + LatexMathCommand.findByDisplay(c)?.firstOrNull() ?: LatexRegularCommand.findByDisplay(c)?.firstOrNull() } else { LatexRegularCommand.findByDisplay(c)?.firstOrNull() diff --git a/src/nl/hannahsten/texifyidea/lang/commands/LatexSiunitxCommand.kt b/src/nl/hannahsten/texifyidea/lang/commands/LatexSiunitxCommand.kt index 72c512babe..d2a1734019 100644 --- a/src/nl/hannahsten/texifyidea/lang/commands/LatexSiunitxCommand.kt +++ b/src/nl/hannahsten/texifyidea/lang/commands/LatexSiunitxCommand.kt @@ -19,6 +19,7 @@ enum class LatexSiunitxCommand( NUM("num", "options".asOptional(), "number".asRequired(), dependency = SIUNITX), UNIT("unit", "options".asOptional(), "unit".asRequired(), dependency = SIUNITX), QTY("qty", "options".asOptional(), "number".asRequired(), "unit".asRequired(), dependency = SIUNITX), + MICRO("micro", display = "µ", dependency = SIUNITX), NUMLIST("numlist", "options".asOptional(), "numbers".asRequired(), dependency = SIUNITX), NUMPRODUCT("numproduct", "options".asOptional(), "numbers".asRequired(), dependency = SIUNITX), NUMRANGE("numrange", "options".asOptional(), "number1".asRequired(), "number2".asRequired(), dependency = SIUNITX), diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexUnicodeInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexUnicodeInspectionTest.kt index 8431a79096..f33d455a4a 100644 --- a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexUnicodeInspectionTest.kt +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexUnicodeInspectionTest.kt @@ -95,6 +95,13 @@ class LatexUnicodeInspectionQuickFix : LatexUnicodeInspectionTest() { testNamedQuickFix("é", "\\'e", "Escape Unicode character", 2) } + fun `test mu`() { + setUnicodeSupport(false) + + testNamedQuickFix("$ µ$", "$ \\micro$", "Escape Unicode character", 1) + testNamedQuickFix("$ μ$", "$ \\mu$", "Escape Unicode character", 1) + } + @Suppress("NonAsciiCharacters") fun `test escape unicode quick fix î`() { setUnicodeSupport(false)