From c26af029bbd37c1ca49b049538438ebc30fee0e2 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sat, 14 Dec 2024 15:27:20 +0100 Subject: [PATCH 1/3] Add command redefinitions to command definition filter in structure view, fix #3661 --- CHANGELOG.md | 1 + .../texifyidea/structure/filter/CommandDefinitionFilter.kt | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28bd56da3..5bc7236e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] ### Added +* Add command redefinitions to command definition filter in structure view * Add inspection to warn about a missing reference for a glossary occurrence * Do not fold sections in a command definition * Include optional parameters in spellcheck, if it contains text diff --git a/src/nl/hannahsten/texifyidea/structure/filter/CommandDefinitionFilter.kt b/src/nl/hannahsten/texifyidea/structure/filter/CommandDefinitionFilter.kt index e88ce7297..3a105d93f 100644 --- a/src/nl/hannahsten/texifyidea/structure/filter/CommandDefinitionFilter.kt +++ b/src/nl/hannahsten/texifyidea/structure/filter/CommandDefinitionFilter.kt @@ -18,8 +18,7 @@ class CommandDefinitionFilter : Filter { true } else !( - treeElement.commandName == "\\newcommand" || - treeElement.commandName in CommandMagic.mathCommandDefinitions || + treeElement.commandName in CommandMagic.commandDefinitionsAndRedefinitions || treeElement.presentation is LatexOtherCommandPresentation ) } From ac592d1a1b8d2d4500312fd0593af868467776ba Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sun, 15 Dec 2024 10:12:06 +0100 Subject: [PATCH 2/3] Convert java to kotlin in LatexStructureViewElement --- .../latex/LatexStructureViewElement.kt | 73 +++++++------------ .../structure/latex/SectionNumbering.kt | 4 +- 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt b/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt index 1a56359d9..5bc554bdd 100644 --- a/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt +++ b/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt @@ -22,6 +22,7 @@ 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.magic.CommandMagic +import nl.hannahsten.texifyidea.util.magic.cmd import nl.hannahsten.texifyidea.util.parser.allCommands import nl.hannahsten.texifyidea.util.parser.getIncludedFiles import java.util.* @@ -79,18 +80,18 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView // Fetch all commands in the active file. val numbering = SectionNumbering(DocumentClass.getClassByName(docClass)) val commands = element.allCommands() - val treeElements = ArrayList() + val treeElements = ArrayList() // Add includes. addIncludes(treeElements, commands) // Add sectioning. - val sections = ArrayDeque() + val sections = mutableListOf() for (currentCmd in commands) { val token = currentCmd.name // Update counter. - if (token == "\\addtocounter" || token == "\\setcounter") { + if (token == LatexGenericRegularCommand.ADDTOCOUNTER.cmd || token == LatexGenericRegularCommand.SETCOUNTER.cmd) { updateNumbering(currentCmd, numbering) continue } @@ -108,13 +109,13 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView // First section. if (sections.isEmpty()) { - sections.addFirst(child) + sections.add(child) treeElements.add(child) setLevelHint(child, numbering) continue } - val currentIndex = order(current(sections) ?: continue) + val currentIndex = order(sections.lastOrNull() ?: continue) val nextIndex = order(currentCmd) when { @@ -133,12 +134,12 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView addFromLabelingCommands(treeElements, commands) // Add bibitem definitions. - addFromCommand(treeElements, commands, "\\bibitem") + addFromCommand(treeElements, commands, LatexGenericRegularCommand.BIBITEM.cmd) - return treeElements.toTypedArray() + return treeElements.sortedBy { it.value.textOffset }.toTypedArray() } - private fun addIncludes(treeElements: MutableList, commands: List) { + private fun addIncludes(treeElements: MutableList, commands: List) { for (command in commands) { if (command.name !in getIncludeCommands()) { continue @@ -158,7 +159,7 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView } private fun addFromCommand( - treeElements: MutableList, commands: List, + treeElements: MutableList, commands: List, commandName: String ) { for (cmd in commands) { @@ -168,9 +169,12 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView } } - private fun addFromLabelingCommands(treeElements: MutableList, commands: List) { + /** + * Add commands which define new labels + */ + private fun addFromLabelingCommands(treeElements: MutableList, commands: List) { val labelingCommands = getLabelDefinitionCommands() - commands.filter { labelingCommands.contains(it.commandToken.text) } + commands.filter { labelingCommands.contains(it.name) } .mapNotNull { LatexStructureViewCommandElement.newCommand(it) } .forEach { treeElements.add(it) @@ -178,16 +182,16 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView } private fun registerHigher( - sections: Deque, + sections: MutableList, child: LatexStructureViewCommandElement, currentCmd: LatexCommands, - treeElements: MutableList, + treeElements: MutableList, numbering: SectionNumbering ) { val indexInsert = order(currentCmd) - while (!sections.isEmpty()) { - pop(sections) - val index = current(sections)?.let { order(it) } + while (sections.isNotEmpty()) { + sections.removeLastOrNull() + val index = sections.lastOrNull()?.let { order(it) } if (index != null && indexInsert > index) { registerDeeper(sections, child, numbering) @@ -203,31 +207,31 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView } private fun registerDeeper( - sections: Deque, + sections: MutableList, child: LatexStructureViewCommandElement, numbering: SectionNumbering ) { - current(sections)?.addChild(child) ?: return - queue(child, sections) + sections.lastOrNull()?.addChild(child) ?: return + sections.add(child) setLevelHint(child, numbering) } private fun registerSameLevel( - sections: Deque, + sections: MutableList, child: LatexStructureViewCommandElement, currentCmd: LatexCommands, - treeElements: MutableList, + treeElements: MutableList, numbering: SectionNumbering ) { - sections.pollFirst() - val parent = sections.peekFirst() + sections.removeLastOrNull() + val parent = sections.lastOrNull() parent?.addChild(child) sections.addFirst(child) setLevelHint(child, numbering) - if (currentCmd.commandToken.text == highestLevel(sections)) { + if (currentCmd.name == sections.minBy { order(it) }.commandName) { treeElements.add(child) } } @@ -273,27 +277,6 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView .anyMatch { l -> l.elementType == LatexTypes.STAR } } - private fun highestLevel(sections: Deque): String { - return sections.stream() - .map { this.order(it) } - .min { obj, anotherInteger -> obj.compareTo(anotherInteger) } - .map { CommandMagic.sectionMarkers[it] } - .orElse("\\section") - } - - private fun pop(sections: Deque) { - sections.removeFirst() - } - - private fun queue( - child: LatexStructureViewCommandElement, - sections: Deque - ) { - sections.addFirst(child) - } - - private fun current(sections: Deque) = sections.peekFirst() ?: null - private fun order(element: LatexStructureViewCommandElement) = order(element.commandName) private fun order(commands: LatexCommands) = order(commands.commandToken.text) diff --git a/src/nl/hannahsten/texifyidea/structure/latex/SectionNumbering.kt b/src/nl/hannahsten/texifyidea/structure/latex/SectionNumbering.kt index 271317443..5ba236965 100644 --- a/src/nl/hannahsten/texifyidea/structure/latex/SectionNumbering.kt +++ b/src/nl/hannahsten/texifyidea/structure/latex/SectionNumbering.kt @@ -33,8 +33,6 @@ class SectionNumbering(private val documentClass: DocumentClass) { } } - private fun getCounter(level: Int) = counters[level] - fun setCounter(level: Int, amount: Int) { counters[level] = amount } @@ -54,7 +52,7 @@ class SectionNumbering(private val documentClass: DocumentClass) { for (i in documentClass.startIndex..level) { sb.append(delimiter) - sb.append(getCounter(i)) + sb.append(counters[i]) delimiter = "." } From df8ff419e59c8eae5897a679afc9d2f844c3845e Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sun, 15 Dec 2024 11:47:09 +0100 Subject: [PATCH 3/3] Change order in structure view to match source file and sectioning level --- CHANGELOG.md | 1 + .../latex/LatexStructureViewElement.kt | 154 ++++++++---------- .../latex/LatexStructureViewElementTest.kt | 26 +++ 3 files changed, 98 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc7236e9..7176ec89a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] ### Added +* Change order in structure view to match source file and sectioning level * Add command redefinitions to command definition filter in structure view * 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/structure/latex/LatexStructureViewElement.kt b/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt index 5bc554bdd..b958116a0 100644 --- a/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt +++ b/src/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElement.kt @@ -82,103 +82,89 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView val commands = element.allCommands() val treeElements = ArrayList() - // Add includes. - addIncludes(treeElements, commands) + val includeCommands = getIncludeCommands() + val labelingCommands = getLabelDefinitionCommands() // Add sectioning. val sections = mutableListOf() - for (currentCmd in commands) { - val token = currentCmd.name - + for (command in commands) { // Update counter. - if (token == LatexGenericRegularCommand.ADDTOCOUNTER.cmd || token == LatexGenericRegularCommand.SETCOUNTER.cmd) { - updateNumbering(currentCmd, numbering) - continue - } - - // Only consider section markers. - if (!CommandMagic.sectionMarkers.contains(token)) { - continue - } - - if (currentCmd.getRequiredParameters().isEmpty()) { - continue - } - - val child = LatexStructureViewCommandElement.newCommand(currentCmd) ?: continue - - // First section. - if (sections.isEmpty()) { - sections.add(child) - treeElements.add(child) - setLevelHint(child, numbering) + if (command.name == LatexGenericRegularCommand.ADDTOCOUNTER.cmd || command.name == LatexGenericRegularCommand.SETCOUNTER.cmd) { + updateNumbering(command, numbering) continue } - val currentIndex = order(sections.lastOrNull() ?: continue) - val nextIndex = order(currentCmd) + val newElement = LatexStructureViewCommandElement.newCommand(command) ?: continue - when { - currentIndex == nextIndex -> registerSameLevel(sections, child, currentCmd, treeElements, numbering) - nextIndex > currentIndex -> registerDeeper(sections, child, numbering) - else -> registerHigher(sections, child, currentCmd, treeElements, numbering) + when (command.name) { + in CommandMagic.sectionMarkers -> { + addSections(command, sections, treeElements, numbering) + } + in labelingCommands + CommandMagic.commandDefinitionsAndRedefinitions + setOf(LatexGenericRegularCommand.BIBITEM.cmd) -> { + addAtCurrentSectionLevel(sections, treeElements, newElement) + } + in includeCommands -> { + for (psiFile in command.getIncludedFiles(includeInstalledPackages = TexifySettings.getInstance().showPackagesInStructureView)) { + if (BibtexFileType == psiFile.fileType) { + newElement.addChild(BibtexStructureViewElement(psiFile)) + } + else if (LatexFileType == psiFile.fileType || StyleFileType == psiFile.fileType) { + newElement.addChild(LatexStructureViewElement(psiFile)) + } + } + + addAtCurrentSectionLevel(sections, treeElements, newElement) + } } } - - // Add command definitions. - CommandMagic.commandDefinitionsAndRedefinitions.forEach { - addFromCommand(treeElements, commands, it) - } - - // Add label definitions. - addFromLabelingCommands(treeElements, commands) - - // Add bibitem definitions. - addFromCommand(treeElements, commands, LatexGenericRegularCommand.BIBITEM.cmd) - return treeElements.sortedBy { it.value.textOffset }.toTypedArray() } - private fun addIncludes(treeElements: MutableList, commands: List) { - for (command in commands) { - if (command.name !in getIncludeCommands()) { - continue - } - - val elt = LatexStructureViewCommandElement.newCommand(command) ?: continue - for (psiFile in command.getIncludedFiles(includeInstalledPackages = TexifySettings.getInstance().showPackagesInStructureView)) { - if (BibtexFileType == psiFile.fileType) { - elt.addChild(BibtexStructureViewElement(psiFile)) - } - else if (LatexFileType == psiFile.fileType || StyleFileType == psiFile.fileType) { - elt.addChild(LatexStructureViewElement(psiFile)) - } - } - treeElements.add(elt) + /** + * Add to top level or at the current sectioning level, so that all entries in the structure view are in the same order as they are in the source + */ + private fun addAtCurrentSectionLevel( + sections: MutableList, + treeElements: ArrayList, + newElement: LatexStructureViewCommandElement + ) { + if (sections.isNotEmpty()) { + sections.last().addChild(newElement) + } + else { + treeElements.add(newElement) } } - private fun addFromCommand( - treeElements: MutableList, commands: List, - commandName: String + private fun LatexStructureViewElement.addSections( + command: LatexCommands, + sections: MutableList, + treeElements: ArrayList, + numbering: SectionNumbering ) { - for (cmd in commands) { - if (cmd.commandToken.text != commandName) continue - val element = LatexStructureViewCommandElement.newCommand(cmd) ?: continue - treeElements.add(element) + if (command.getRequiredParameters().isEmpty()) { + return } - } - /** - * Add commands which define new labels - */ - private fun addFromLabelingCommands(treeElements: MutableList, commands: List) { - val labelingCommands = getLabelDefinitionCommands() - commands.filter { labelingCommands.contains(it.name) } - .mapNotNull { LatexStructureViewCommandElement.newCommand(it) } - .forEach { - treeElements.add(it) - } + val child = LatexStructureViewCommandElement.newCommand(command) ?: return + + // First section. + if (sections.isEmpty()) { + sections.add(child) + treeElements.add(child) + setLevelHint(child, numbering) + return + } + + // Order of the most recently added element, which is kept at the end of the list for administrative purposes + val currentIndex = order(sections.lastOrNull() ?: return) + val nextIndex = order(command) + + when { + currentIndex == nextIndex -> registerSameLevel(sections, child, command, treeElements, numbering) + nextIndex > currentIndex -> registerDeeper(sections, child, numbering) + else -> registerHigher(sections, child, command, treeElements, numbering) + } } private fun registerHigher( @@ -188,18 +174,19 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView treeElements: MutableList, numbering: SectionNumbering ) { - val indexInsert = order(currentCmd) + val currentOrder = order(currentCmd) while (sections.isNotEmpty()) { + // The last entry is the most recently added element (as a child somewhere), remove it first sections.removeLastOrNull() - val index = sections.lastOrNull()?.let { order(it) } + val highestLevelOrder = sections.lastOrNull()?.let { order(it) } - if (index != null && indexInsert > index) { + if (highestLevelOrder != null && currentOrder > highestLevelOrder) { registerDeeper(sections, child, numbering) break } // Avoid that an element is not added at all by adding it one level up anyway. // If index is null, that means that the tree currently only has elements with a higher order. - else if (index == null || indexInsert == index) { + else if (highestLevelOrder == null || currentOrder == highestLevelOrder) { registerSameLevel(sections, child, currentCmd, treeElements, numbering) break } @@ -224,10 +211,11 @@ class LatexStructureViewElement(private val element: PsiElement) : StructureView treeElements: MutableList, numbering: SectionNumbering ) { + // The last entry is the most recently added element (as a child somewhere), remove it first sections.removeLastOrNull() val parent = sections.lastOrNull() parent?.addChild(child) - sections.addFirst(child) + sections.add(child) setLevelHint(child, numbering) diff --git a/test/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElementTest.kt b/test/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElementTest.kt index 7a99c64b2..239881ba5 100644 --- a/test/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElementTest.kt +++ b/test/nl/hannahsten/texifyidea/structure/latex/LatexStructureViewElementTest.kt @@ -57,4 +57,30 @@ class LatexStructureViewElementTest : BasePlatformTestCase() { ) } } + + fun `test labels are added in the correct place`() { + myFixture.configureByText( + LatexFileType, + """ + \section{s} + \subsection{ss1} + + \label{l} + + \subsection{ss2} + """.trimIndent() + ) + + myFixture.testStructureView { component -> + val documentChildren = (component.treeModel as LatexStructureViewModel).root.children + assertEquals( + listOf("\\section{s}"), + documentChildren.map { (it as LatexStructureViewCommandElement).value.text } + ) + assertEquals( + listOf("\\label{l}"), + documentChildren.filterIsInstance().first().children.first().children.map { (it as LatexStructureViewCommandElement).value.text } + ) + } + } } \ No newline at end of file