From 45908ff6289d76993d73c1779057446652fc4702 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sun, 15 Dec 2024 21:17:38 +0100 Subject: [PATCH 1/4] Add support for automatic detection of custom command aliases for include commands --- CHANGELOG.md | 1 + .../gutter/LatexNavigationGutter.kt | 5 +- .../texifyidea/highlighting/LatexAnnotator.kt | 4 +- .../stub/LatexCommandsStubElementType.kt | 8 +-- .../texifyidea/lang/alias/AliasManager.kt | 4 +- .../structure/filter/IncludesFilter.kt | 4 +- .../structure/filter/LabelFilter.kt | 4 +- .../latex/LatexIncludePresentation.kt | 4 +- .../structure/latex/LatexLabelPresentation.kt | 4 +- .../latex/LatexPresentationFactory.kt | 4 +- .../latex/LatexStructureViewElement.kt | 8 +-- .../texifyidea/util/CommandAlias.kt | 49 +++++++++++++++++++ src/nl/hannahsten/texifyidea/util/Commands.kt | 15 +++--- .../texifyidea/util/labels/ProjectLabels.kt | 2 +- .../texifyidea/util/magic/CommandMagic.kt | 21 +------- .../util/parser/LatexCommandsImplMixinUtil.kt | 7 ++- .../LatexFileNotFoundInspectionTest.kt | 8 +++ 17 files changed, 99 insertions(+), 53 deletions(-) create mode 100644 src/nl/hannahsten/texifyidea/util/CommandAlias.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 74f38eaa6..2b8d31df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] ### Added +* Add support for automatic detection of custom command aliases for include commands * Add support for DeclareGraphicsExtensions * Add inspection to warn about a missing reference for a glossary occurrence * Do not fold sections in a command definition diff --git a/src/nl/hannahsten/texifyidea/gutter/LatexNavigationGutter.kt b/src/nl/hannahsten/texifyidea/gutter/LatexNavigationGutter.kt index 3a8efa32e..915512051 100644 --- a/src/nl/hannahsten/texifyidea/gutter/LatexNavigationGutter.kt +++ b/src/nl/hannahsten/texifyidea/gutter/LatexNavigationGutter.kt @@ -13,6 +13,7 @@ import nl.hannahsten.texifyidea.psi.LatexCommands import nl.hannahsten.texifyidea.psi.LatexRequiredParamContent import nl.hannahsten.texifyidea.reference.InputFileReference import nl.hannahsten.texifyidea.util.Log +import nl.hannahsten.texifyidea.util.getOriginalCommandFromAlias import nl.hannahsten.texifyidea.util.parser.parentOfType import nl.hannahsten.texifyidea.util.parser.requiredParameters import javax.swing.Icon @@ -36,9 +37,9 @@ class LatexNavigationGutter : RelatedItemLineMarkerProvider() { val fullCommand = command.name ?: return // Fetch the corresponding LatexRegularCommand object. - val commandHuh = LatexCommand.lookup(fullCommand.substring(1)) ?: return + val commandHuh = LatexCommand.lookup(fullCommand.substring(1))?.first() ?: getOriginalCommandFromAlias(command.name ?: return, command.project) ?: return - val arguments = commandHuh.first().getArgumentsOf(RequiredFileArgument::class.java) + val arguments = commandHuh.getArgumentsOf(RequiredFileArgument::class.java) if (arguments.isEmpty()) { return } diff --git a/src/nl/hannahsten/texifyidea/highlighting/LatexAnnotator.kt b/src/nl/hannahsten/texifyidea/highlighting/LatexAnnotator.kt index 959f8181a..fdfe025a0 100644 --- a/src/nl/hannahsten/texifyidea/highlighting/LatexAnnotator.kt +++ b/src/nl/hannahsten/texifyidea/highlighting/LatexAnnotator.kt @@ -16,7 +16,7 @@ import nl.hannahsten.texifyidea.lang.commands.LatexGenericMathCommand.* import nl.hannahsten.texifyidea.lang.commands.LatexGenericRegularCommand import nl.hannahsten.texifyidea.lang.commands.LatexGenericRegularCommand.* import nl.hannahsten.texifyidea.psi.* -import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommands +import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommandsNoUpdate import nl.hannahsten.texifyidea.util.magic.CommandMagic import nl.hannahsten.texifyidea.util.magic.cmd import nl.hannahsten.texifyidea.util.parser.* @@ -221,7 +221,7 @@ open class LatexAnnotator : Annotator { LatexSyntaxHighlighter.LABEL_REFERENCE } // Label definitions. - in getLabelDefinitionCommands() -> { + in getLabelDefinitionCommandsNoUpdate() -> { LatexSyntaxHighlighter.LABEL_DEFINITION } // Bibliography references (citations). diff --git a/src/nl/hannahsten/texifyidea/index/stub/LatexCommandsStubElementType.kt b/src/nl/hannahsten/texifyidea/index/stub/LatexCommandsStubElementType.kt index 138ec42ab..f08a07d11 100644 --- a/src/nl/hannahsten/texifyidea/index/stub/LatexCommandsStubElementType.kt +++ b/src/nl/hannahsten/texifyidea/index/stub/LatexCommandsStubElementType.kt @@ -9,7 +9,7 @@ import nl.hannahsten.texifyidea.index.* import nl.hannahsten.texifyidea.index.file.LatexIndexableSetContributor import nl.hannahsten.texifyidea.psi.LatexCommands import nl.hannahsten.texifyidea.psi.impl.LatexCommandsImpl -import nl.hannahsten.texifyidea.util.getIncludeCommands +import nl.hannahsten.texifyidea.util.defaultIncludeCommands import nl.hannahsten.texifyidea.util.magic.CommandMagic import nl.hannahsten.texifyidea.util.parser.toStringMap import java.io.IOException @@ -33,8 +33,8 @@ class LatexCommandsStubElementType(debugName: String) : return latexCommandsStub } - override fun setName(name: String): PsiElement { - this.name = name + override fun setName(newName: String): PsiElement { + this.name = newName return this } @@ -107,7 +107,7 @@ class LatexCommandsStubElementType(debugName: String) : val token = latexCommandsStub.commandToken indexSinkOccurrence(indexSink, LatexCommandsIndex.Util, token) - if (token in getIncludeCommands()) { + if (token in defaultIncludeCommands) { indexSinkOccurrence(indexSink, LatexIncludesIndex.Util, token) } if (token in CommandMagic.definitions) { diff --git a/src/nl/hannahsten/texifyidea/lang/alias/AliasManager.kt b/src/nl/hannahsten/texifyidea/lang/alias/AliasManager.kt index ad71a7b7b..466be72c3 100644 --- a/src/nl/hannahsten/texifyidea/lang/alias/AliasManager.kt +++ b/src/nl/hannahsten/texifyidea/lang/alias/AliasManager.kt @@ -139,8 +139,8 @@ abstract class AliasManager { // Check if something has changed (the number of indexed command might be the same while the content is different), and if so, update the aliases. // Also do this the first time something is registered, because then we have to update aliases as well - val hasNotChanged = this.indexedCommandDefinitions == indexedCommandDefinitions - if (!hasNotChanged || wasRegistered) { + val hasChanged = this.indexedCommandDefinitions != indexedCommandDefinitions + if (hasChanged || wasRegistered) { // Update everything, since it is difficult to know beforehand what aliases could be added or not // Alternatively we could save a numberOfIndexedCommandDefinitions per alias set, and only update the // requested alias set (otherwise only the first alias set requesting an update will get it) diff --git a/src/nl/hannahsten/texifyidea/structure/filter/IncludesFilter.kt b/src/nl/hannahsten/texifyidea/structure/filter/IncludesFilter.kt index f9946f157..0e550a43d 100644 --- a/src/nl/hannahsten/texifyidea/structure/filter/IncludesFilter.kt +++ b/src/nl/hannahsten/texifyidea/structure/filter/IncludesFilter.kt @@ -5,7 +5,7 @@ import com.intellij.ide.util.treeView.smartTree.Filter import com.intellij.ide.util.treeView.smartTree.TreeElement import nl.hannahsten.texifyidea.TexifyIcons import nl.hannahsten.texifyidea.structure.latex.LatexStructureViewCommandElement -import nl.hannahsten.texifyidea.util.getIncludeCommands +import nl.hannahsten.texifyidea.util.updateAndGetIncludeCommands /** * @author Hannah Schellekens @@ -16,7 +16,7 @@ class IncludesFilter : Filter { return if (treeElement !is LatexStructureViewCommandElement) { true } - else treeElement.commandName !in getIncludeCommands() + else treeElement.commandName !in updateAndGetIncludeCommands(treeElement.value.project) } override fun isReverted() = true diff --git a/src/nl/hannahsten/texifyidea/structure/filter/LabelFilter.kt b/src/nl/hannahsten/texifyidea/structure/filter/LabelFilter.kt index 956c98a74..3e70cbda0 100644 --- a/src/nl/hannahsten/texifyidea/structure/filter/LabelFilter.kt +++ b/src/nl/hannahsten/texifyidea/structure/filter/LabelFilter.kt @@ -5,7 +5,7 @@ import com.intellij.ide.util.treeView.smartTree.Filter import com.intellij.ide.util.treeView.smartTree.TreeElement import nl.hannahsten.texifyidea.TexifyIcons import nl.hannahsten.texifyidea.structure.latex.LatexStructureViewCommandElement -import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommands +import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommandsNoUpdate import javax.swing.Icon /** @@ -17,7 +17,7 @@ class LabelFilter : Filter { if (treeElement !is LatexStructureViewCommandElement) { return true } - return !getLabelDefinitionCommands().contains(treeElement.commandName) + return !getLabelDefinitionCommandsNoUpdate().contains(treeElement.commandName) } override fun isReverted(): Boolean = true diff --git a/src/nl/hannahsten/texifyidea/structure/latex/LatexIncludePresentation.kt b/src/nl/hannahsten/texifyidea/structure/latex/LatexIncludePresentation.kt index e5e06d3cb..c3bcadd1d 100644 --- a/src/nl/hannahsten/texifyidea/structure/latex/LatexIncludePresentation.kt +++ b/src/nl/hannahsten/texifyidea/structure/latex/LatexIncludePresentation.kt @@ -3,8 +3,8 @@ package nl.hannahsten.texifyidea.structure.latex import com.intellij.navigation.ItemPresentation import nl.hannahsten.texifyidea.TexifyIcons import nl.hannahsten.texifyidea.psi.LatexCommands -import nl.hannahsten.texifyidea.util.getIncludeCommands import nl.hannahsten.texifyidea.util.parser.getIncludedFiles +import nl.hannahsten.texifyidea.util.updateAndGetIncludeCommands /** * @author Hannah Schellekens @@ -14,7 +14,7 @@ class LatexIncludePresentation(labelCommand: LatexCommands) : ItemPresentation { private val fileName: String init { - if (labelCommand.name !in getIncludeCommands()) { + if (labelCommand.name !in updateAndGetIncludeCommands(labelCommand.project)) { throw IllegalArgumentException("Command $labelCommand is no include command") } diff --git a/src/nl/hannahsten/texifyidea/structure/latex/LatexLabelPresentation.kt b/src/nl/hannahsten/texifyidea/structure/latex/LatexLabelPresentation.kt index 0b350f62f..8572f1697 100644 --- a/src/nl/hannahsten/texifyidea/structure/latex/LatexLabelPresentation.kt +++ b/src/nl/hannahsten/texifyidea/structure/latex/LatexLabelPresentation.kt @@ -5,7 +5,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager import nl.hannahsten.texifyidea.TexifyIcons import nl.hannahsten.texifyidea.lang.alias.CommandManager import nl.hannahsten.texifyidea.psi.LatexCommands -import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommands +import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommandsNoUpdate import nl.hannahsten.texifyidea.util.parser.requiredParameter /** @@ -17,7 +17,7 @@ class LatexLabelPresentation(labelCommand: LatexCommands) : ItemPresentation { private val presentableText: String init { - val labelingCommands = getLabelDefinitionCommands() + val labelingCommands = getLabelDefinitionCommandsNoUpdate() if (!labelingCommands.contains(labelCommand.commandToken.text)) { val token = labelCommand.commandToken.text throw IllegalArgumentException("command '$token' is no \\label-command") diff --git a/src/nl/hannahsten/texifyidea/structure/latex/LatexPresentationFactory.kt b/src/nl/hannahsten/texifyidea/structure/latex/LatexPresentationFactory.kt index 23b48dcfc..d45eb5b7e 100644 --- a/src/nl/hannahsten/texifyidea/structure/latex/LatexPresentationFactory.kt +++ b/src/nl/hannahsten/texifyidea/structure/latex/LatexPresentationFactory.kt @@ -8,9 +8,9 @@ import nl.hannahsten.texifyidea.lang.commands.LatexNewDefinitionCommand.NEWCOMMA import nl.hannahsten.texifyidea.lang.commands.LatexNewDefinitionCommand.RENEWCOMMAND import nl.hannahsten.texifyidea.lang.commands.LatexXparseCommand import nl.hannahsten.texifyidea.psi.LatexCommands -import nl.hannahsten.texifyidea.util.getIncludeCommands import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommands import nl.hannahsten.texifyidea.util.magic.cmd +import nl.hannahsten.texifyidea.util.updateAndGetIncludeCommands /** * @author Hannah Schellekens @@ -37,7 +37,7 @@ object LatexPresentationFactory { ) LABEL.cmd -> LatexLabelPresentation(commands) BIBITEM.cmd -> BibitemPresentation(commands) - in getIncludeCommands() -> LatexIncludePresentation(commands) + in updateAndGetIncludeCommands(commands.project) -> LatexIncludePresentation(commands) else -> LatexOtherCommandPresentation(commands, TexifyIcons.DOT_COMMAND) } } diff --git a/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt b/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt index 1a56359d9..c4e421cda 100644 --- a/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt +++ b/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt @@ -19,11 +19,11 @@ import nl.hannahsten.texifyidea.psi.LatexTypes import nl.hannahsten.texifyidea.settings.TexifySettings import nl.hannahsten.texifyidea.structure.bibtex.BibtexStructureViewElement import nl.hannahsten.texifyidea.structure.latex.SectionNumbering.DocumentClass -import nl.hannahsten.texifyidea.util.getIncludeCommands -import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommands +import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommandsNoUpdate import nl.hannahsten.texifyidea.util.magic.CommandMagic import nl.hannahsten.texifyidea.util.parser.allCommands import nl.hannahsten.texifyidea.util.parser.getIncludedFiles +import nl.hannahsten.texifyidea.util.updateAndGetIncludeCommands import java.util.* /** @@ -140,7 +140,7 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView private fun addIncludes(treeElements: MutableList, commands: List) { for (command in commands) { - if (command.name !in getIncludeCommands()) { + if (command.name !in updateAndGetIncludeCommands(command.project)) { continue } @@ -169,7 +169,7 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView } private fun addFromLabelingCommands(treeElements: MutableList, commands: List) { - val labelingCommands = getLabelDefinitionCommands() + val labelingCommands = getLabelDefinitionCommandsNoUpdate() commands.filter { labelingCommands.contains(it.commandToken.text) } .mapNotNull { LatexStructureViewCommandElement.newCommand(it) } .forEach { diff --git a/src/nl/hannahsten/texifyidea/util/CommandAlias.kt b/src/nl/hannahsten/texifyidea/util/CommandAlias.kt new file mode 100644 index 000000000..50ae8bc20 --- /dev/null +++ b/src/nl/hannahsten/texifyidea/util/CommandAlias.kt @@ -0,0 +1,49 @@ +package nl.hannahsten.texifyidea.util + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.project.Project +import nl.hannahsten.texifyidea.lang.alias.CommandManager +import nl.hannahsten.texifyidea.lang.commands.LatexCommand +import java.util.concurrent.atomic.AtomicBoolean + + +// Due to the update method being called many times, we need to limit the number of updates requested +var isUpdatingIncludeAliases = AtomicBoolean(false) + +fun updateAndGetIncludeCommands(project: Project): Set { + // For performance reasons, do not wait until the update (which requires index access) is done + updateIncludeCommandsAliasesAsync(project) + return defaultIncludeCommands.map { CommandManager.getAliases(it) }.flatten().toSet() +} + +fun updateIncludeCommandsAliasesAsync(project: Project) { + if (!isUpdatingIncludeAliases.getAndSet(true)) { + // Don't run with progress indicator, because this takes a short time (a few tenths) and runs in practice on every letter typed + ApplicationManager.getApplication().invokeLater { + try { + // Because every command has different parameters and behaviour (e.g. allowed file types), we keep track of them separately + for (command in defaultIncludeCommands) { + runReadAction { + CommandManager.updateAliases(setOf(command), project) + } + } + } + finally { + isUpdatingIncludeAliases.set(false) + } + } + } +} + +/** + * Given a possible alias of an include command, find a random original command it is an alias of + */ +fun getOriginalCommandFromAlias(commandName: String, project: Project): LatexCommand? { + val aliasSet = CommandManager.getAliases(commandName) + if (aliasSet.isEmpty()) { + // If we can't find anything, trigger an update so that maybe we have the information next time + updateIncludeCommandsAliasesAsync(project) + } + return LatexCommand.lookup(aliasSet.firstOrNull { it in defaultIncludeCommands })?.first() +} \ No newline at end of file diff --git a/src/nl/hannahsten/texifyidea/util/Commands.kt b/src/nl/hannahsten/texifyidea/util/Commands.kt index 69b07b51c..faa99d310 100644 --- a/src/nl/hannahsten/texifyidea/util/Commands.kt +++ b/src/nl/hannahsten/texifyidea/util/Commands.kt @@ -32,14 +32,15 @@ fun Project.findCommandDefinitions(): Collection { } /** - * Get all commands that include other files, including backslashes. + * Get all commands that include other files, including backslashes. Use [updateAndGetIncludeCommands] to include custom commands, if possible. */ -fun getIncludeCommands(): Set { - return LatexRegularCommand.values() - .filter { command -> command.arguments.any { it is RequiredFileArgument } } - .map { "\\" + it.command } - .toSet() -} +val defaultIncludeCommands: Set + by lazy { + LatexRegularCommand.values() + .filter { command -> command.arguments.any { it is RequiredFileArgument } } + .map { "\\" + it.command } + .toSet() + } /** * Inserts a custom c custom command definition. diff --git a/src/nl/hannahsten/texifyidea/util/labels/ProjectLabels.kt b/src/nl/hannahsten/texifyidea/util/labels/ProjectLabels.kt index 8c5699ce9..a38f704f8 100644 --- a/src/nl/hannahsten/texifyidea/util/labels/ProjectLabels.kt +++ b/src/nl/hannahsten/texifyidea/util/labels/ProjectLabels.kt @@ -37,7 +37,7 @@ fun Project.getLabelReferenceCommands(): Set { /** * Get all commands defining labels, including user defined commands. This will not check if the aliases need to be updated. */ -fun getLabelDefinitionCommands() = CommandManager.getAliases(CommandMagic.labelDefinitionsWithoutCustomCommands.first()) +fun getLabelDefinitionCommandsNoUpdate() = CommandManager.getAliases(CommandMagic.labelDefinitionsWithoutCustomCommands.first()) /** * Get all commands defining labels, including user defined commands. diff --git a/src/nl/hannahsten/texifyidea/util/magic/CommandMagic.kt b/src/nl/hannahsten/texifyidea/util/magic/CommandMagic.kt index 46faf3e9c..948615a13 100644 --- a/src/nl/hannahsten/texifyidea/util/magic/CommandMagic.kt +++ b/src/nl/hannahsten/texifyidea/util/magic/CommandMagic.kt @@ -2,9 +2,7 @@ package nl.hannahsten.texifyidea.util.magic -import com.intellij.openapi.project.Project import com.intellij.ui.Gray -import nl.hannahsten.texifyidea.lang.alias.CommandManager import nl.hannahsten.texifyidea.lang.commands.Argument import nl.hannahsten.texifyidea.lang.commands.LatexBiblatexCommand.* import nl.hannahsten.texifyidea.lang.commands.LatexCommand @@ -220,27 +218,10 @@ object CommandMagic { /** * All commands that define labels and that are present by default. - * To include user defined commands, use [getLabelDefinitionCommands] (may be significantly slower). + * To include user defined commands, use [nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommands] (may be significantly slower). */ val labelDefinitionsWithoutCustomCommands = setOf(LABEL.cmd) - /** - * Get all commands defining labels, including user defined commands. - * If you need to know which parameters of user defined commands define a label, use [CommandManager.labelAliasesInfo]. - * - * This will check if the cache of user defined commands needs to be updated, based on the given project, and therefore may take some time. - */ - fun getLabelDefinitionCommands(project: Project): Set { - // Check if updates are needed - CommandManager.updateAliases(labelDefinitionsWithoutCustomCommands, project) - return CommandManager.getAliases(labelDefinitionsWithoutCustomCommands.first()) - } - - /** - * Get all commands defining labels, including user defined commands. This will not check if the aliases need to be updated. - */ - fun getLabelDefinitionCommands() = CommandManager.getAliases(labelDefinitionsWithoutCustomCommands.first()) - /** * All commands that define bibliography items. */ diff --git a/src/nl/hannahsten/texifyidea/util/parser/LatexCommandsImplMixinUtil.kt b/src/nl/hannahsten/texifyidea/util/parser/LatexCommandsImplMixinUtil.kt index 180982770..b35d6981d 100644 --- a/src/nl/hannahsten/texifyidea/util/parser/LatexCommandsImplMixinUtil.kt +++ b/src/nl/hannahsten/texifyidea/util/parser/LatexCommandsImplMixinUtil.kt @@ -12,6 +12,7 @@ import nl.hannahsten.texifyidea.lang.commands.* import nl.hannahsten.texifyidea.psi.* import nl.hannahsten.texifyidea.reference.InputFileReference import nl.hannahsten.texifyidea.reference.LatexLabelReference +import nl.hannahsten.texifyidea.util.getOriginalCommandFromAlias import nl.hannahsten.texifyidea.util.magic.PatternMagic import nl.hannahsten.texifyidea.util.magic.cmd import nl.hannahsten.texifyidea.util.shrink @@ -22,6 +23,7 @@ import kotlin.collections.set /** * Check if the command includes other files, and if so return [InputFileReference] instances for them. + * This method is called continuously, so it should be really fast. * * Use this instead of command.references.filterIsInstance(), to avoid resolving references of types that will not be needed. */ @@ -29,7 +31,10 @@ fun LatexCommands.getFileArgumentsReferences(): List { val inputFileReferences = mutableListOf() // There may be multiple commands with this name, just guess the first one - val command = LatexCommand.lookup(this.name)?.firstOrNull() ?: return emptyList() + val command = LatexCommand.lookup(this.name)?.firstOrNull() + // If not found, maybe it is an alias (user defined command) of a known command + ?: getOriginalCommandFromAlias(this.name ?: return emptyList(), project) + ?: return emptyList() // Arguments from the LatexCommand (so the command as hardcoded in e.g. LatexRegularCommand) val requiredArguments = command.arguments.mapNotNull { it as? RequiredArgument } diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexFileNotFoundInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexFileNotFoundInspectionTest.kt index f3a7322f3..3e13e3fb1 100644 --- a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexFileNotFoundInspectionTest.kt +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexFileNotFoundInspectionTest.kt @@ -7,6 +7,7 @@ import nl.hannahsten.texifyidea.file.LatexFileType import nl.hannahsten.texifyidea.gutter.LatexNavigationGutter import nl.hannahsten.texifyidea.inspections.TexifyInspectionTestBase import nl.hannahsten.texifyidea.util.runCommandWithExitCode +import nl.hannahsten.texifyidea.util.updateIncludeCommandsAliasesAsync import java.io.File import java.nio.file.Path import java.nio.file.Paths @@ -191,4 +192,11 @@ class LatexFileNotFoundInspectionTest : TexifyInspectionTestBase(LatexFileNotFou myFixture.configureByText(LatexFileType, "\\input{included.txt}") myFixture.checkHighlighting() } + + fun testCommandAlias() { + myFixture.configureByText(LatexFileType, """\newcommand{\myinput}{\input} \myinput{doesnotexist.tex}""") + // In practice, this will be triggered by the first something to ask for include commands aliases, for performance reasons + updateIncludeCommandsAliasesAsync(myFixture.project) + myFixture.checkHighlighting() + } } \ No newline at end of file From 7ee99b85d103d9ddbb17b7a1b6b8c06c47382b8f Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sun, 15 Dec 2024 21:24:06 +0100 Subject: [PATCH 2/4] Update documentation --- Writerside/topics/Editing-a-LaTeX-file.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Writerside/topics/Editing-a-LaTeX-file.md b/Writerside/topics/Editing-a-LaTeX-file.md index f67b41e88..7fb145d7f 100644 --- a/Writerside/topics/Editing-a-LaTeX-file.md +++ b/Writerside/topics/Editing-a-LaTeX-file.md @@ -244,7 +244,7 @@ See [https://blog.codinghorror.com/the-problem-with-code-folding/](https://blog. _Since b0.7_ -TeXiFy supports custom definitions of `label`-like, `\ref`-like and `\cite`-like commands. +TeXiFy supports custom definitions (aliases) of `label`-like, `\ref`-like, `\cite`-like and `input`-like commands. For example, if you write @@ -259,7 +259,7 @@ For example, if you write For definitions like `\newcommand{\mycite}[1]{\citeauthor{#1}\cite{#1}}`, this means that you will also get autocompletion of citation labels in `\mycite` commands. -In the case of definitions including a `\label` command, we check the parameter positions as well. +In the case of definitions including a `\label` or any command that has a file parameter, we check the parameter positions as well. For example, From 775cddcf091541e876edcd45281ed0d1e34957ff Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Wed, 18 Dec 2024 13:03:46 +0100 Subject: [PATCH 3/4] Formatting --- src/nl/hannahsten/texifyidea/util/CommandAlias.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nl/hannahsten/texifyidea/util/CommandAlias.kt b/src/nl/hannahsten/texifyidea/util/CommandAlias.kt index 50ae8bc20..f96a42904 100644 --- a/src/nl/hannahsten/texifyidea/util/CommandAlias.kt +++ b/src/nl/hannahsten/texifyidea/util/CommandAlias.kt @@ -7,7 +7,6 @@ import nl.hannahsten.texifyidea.lang.alias.CommandManager import nl.hannahsten.texifyidea.lang.commands.LatexCommand import java.util.concurrent.atomic.AtomicBoolean - // Due to the update method being called many times, we need to limit the number of updates requested var isUpdatingIncludeAliases = AtomicBoolean(false) From 81115fcfbd3437cccc58e02ff04546548e4da036 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Wed, 18 Dec 2024 14:42:05 +0100 Subject: [PATCH 4/4] Fix some edge cases related to a command which can be an alias of both \input and \label --- .../texifyidea/lang/alias/AliasManager.kt | 7 ++++--- .../texifyidea/lang/alias/CommandManager.kt | 4 ++-- .../util/parser/LatexCommandsImplMixinUtil.kt | 2 +- .../LatexFileNotFoundInspectionTest.kt | 7 +++++++ .../texifyidea/lang/CommandManagerTest.kt | 14 +++++++------- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/lang/alias/AliasManager.kt b/src/nl/hannahsten/texifyidea/lang/alias/AliasManager.kt index 466be72c3..e925b4b93 100644 --- a/src/nl/hannahsten/texifyidea/lang/alias/AliasManager.kt +++ b/src/nl/hannahsten/texifyidea/lang/alias/AliasManager.kt @@ -86,14 +86,15 @@ abstract class AliasManager { * @param alias * The alias to register for the item. This could be either * a new item, or an existing item *E.g. `\start`* + * @param isRedefinition If the alias is being redefined, remove the original definition */ @Synchronized - fun registerAlias(item: String, alias: String) { + fun registerAlias(item: String, alias: String, isRedefinition: Boolean = false) { synchronized(aliases) { val aliasSet = aliases[item] ?: mutableSetOf() - // If the alias is already assigned: unassign it. - if (isRegistered(alias)) { + // If the alias is already assigned and we are redefining it: unassign it. + if (isRedefinition && isRegistered(alias)) { val previousAliases = aliases[alias] previousAliases?.remove(alias) aliases.remove(alias) diff --git a/src/nl/hannahsten/texifyidea/lang/alias/CommandManager.kt b/src/nl/hannahsten/texifyidea/lang/alias/CommandManager.kt index c40c19b17..4b6e300d5 100644 --- a/src/nl/hannahsten/texifyidea/lang/alias/CommandManager.kt +++ b/src/nl/hannahsten/texifyidea/lang/alias/CommandManager.kt @@ -134,8 +134,8 @@ object CommandManager : Iterable, Serializable, AliasManager() { * When the given command already exixts. */ @Throws(IllegalArgumentException::class) - fun registerAliasNoSlash(commandNoSlash: String, aliasNoSlash: String) { - registerAlias("\\" + commandNoSlash, "\\" + aliasNoSlash) + fun registerAliasNoSlash(commandNoSlash: String, aliasNoSlash: String, isRedefinition: Boolean = false) { + registerAlias("\\" + commandNoSlash, "\\" + aliasNoSlash, isRedefinition) } override fun findAllAliases( diff --git a/src/nl/hannahsten/texifyidea/util/parser/LatexCommandsImplMixinUtil.kt b/src/nl/hannahsten/texifyidea/util/parser/LatexCommandsImplMixinUtil.kt index b35d6981d..3bd07198a 100644 --- a/src/nl/hannahsten/texifyidea/util/parser/LatexCommandsImplMixinUtil.kt +++ b/src/nl/hannahsten/texifyidea/util/parser/LatexCommandsImplMixinUtil.kt @@ -42,7 +42,7 @@ fun LatexCommands.getFileArgumentsReferences(): List { // Find file references within required parameters and across required parameters (think \referencing{reference1,reference2}{reference3} ) for (i in requiredParameters().indices) { // Find the corresponding requiredArgument - val requiredArgument = if (i < requiredArguments.size) requiredArguments[i] else requiredArguments.lastOrNull { it is RequiredFileArgument } ?: continue + val requiredArgument = if (i < requiredArguments.size) requiredArguments[i] else continue // Check if the actual argument is a file argument or continue with the next argument val fileArgument = requiredArgument as? RequiredFileArgument ?: continue diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexFileNotFoundInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexFileNotFoundInspectionTest.kt index 3e13e3fb1..47fb8d16c 100644 --- a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexFileNotFoundInspectionTest.kt +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexFileNotFoundInspectionTest.kt @@ -199,4 +199,11 @@ class LatexFileNotFoundInspectionTest : TexifyInspectionTestBase(LatexFileNotFou updateIncludeCommandsAliasesAsync(myFixture.project) myFixture.checkHighlighting() } + + fun testCommandAliasMoreParameters() { + myFixture.configureByText(LatexFileType, """\newcommand{\myinput}[2]{\input{#1}\section{#2}} \myinput{doesnotexist.tex}{My section}""") + // In practice, this will be triggered by the first something to ask for include commands aliases, for performance reasons + updateIncludeCommandsAliasesAsync(myFixture.project) + myFixture.checkHighlighting() + } } \ No newline at end of file diff --git a/test/nl/hannahsten/texifyidea/lang/CommandManagerTest.kt b/test/nl/hannahsten/texifyidea/lang/CommandManagerTest.kt index 0b2bcbfae..62f366f9b 100644 --- a/test/nl/hannahsten/texifyidea/lang/CommandManagerTest.kt +++ b/test/nl/hannahsten/texifyidea/lang/CommandManagerTest.kt @@ -244,13 +244,13 @@ class CommandManagerTest { manager!!.registerCommand("\\ten") manager!!.registerAlias("\\one", "\\een") manager!!.registerAlias("\\one", "\\un") - manager!!.registerAlias("\\een", "\\ein") + manager!!.registerAlias("\\een", "\\ein", isRedefinition = true) manager!!.registerAlias("\\two", "\\twee") manager!!.registerAlias("\\twee", "\\deux") manager!!.registerAlias("\\deux", "\\zwei") manager!!.registerAlias("\\three", "\\tien") - manager!!.registerAlias("\\ten", "\\tien") - manager!!.registerAlias("\\tien", "\\dix") + manager!!.registerAlias("\\ten", "\\tien", isRedefinition = true) + manager!!.registerAlias("\\tien", "\\dix", isRedefinition = true) checkDefaultAliases( Function { command: String? -> manager!!.getAliases( @@ -275,7 +275,7 @@ class CommandManagerTest { manager!!.registerAliasNoSlash("twee", "deux") manager!!.registerAliasNoSlash("deux", "zwei") manager!!.registerAliasNoSlash("three", "tien") - manager!!.registerAliasNoSlash("ten", "tien") + manager!!.registerAliasNoSlash("ten", "tien", isRedefinition = true) manager!!.registerAliasNoSlash("tien", "dix") checkDefaultAliases( Function { command: String? -> @@ -535,7 +535,7 @@ class CommandManagerTest { * `\varepsilon => A` */ @Test - fun testEpsilon() { + fun testCommandRedefinition() { resetup(null) // Original commands @@ -544,8 +544,8 @@ class CommandManagerTest { // Set aliases manager!!.registerAliasNoSlash("varepsilon", "goodepsilon") - manager!!.registerAliasNoSlash("epsilon", "varepsilon") - manager!!.registerAliasNoSlash("goodepsilon", "epsilon") + manager!!.registerAliasNoSlash("epsilon", "varepsilon", isRedefinition = true) + manager!!.registerAliasNoSlash("goodepsilon", "epsilon", isRedefinition = true) // Result checking val a = manager!!.getAliasesFromOriginalNoSlash("varepsilon")