From 3da1d9233f002a7975251fdbb032ddbda691c837 Mon Sep 17 00:00:00 2001 From: Abby Berkers Date: Sun, 30 Apr 2023 22:04:35 +0200 Subject: [PATCH 01/10] Add inspection to detect suspicious explicit text formatting --- .../latex/probablebugs/probablebugs.xml | 4 ++ ...exSuspiciousSectionFormattingInspection.kt | 45 +++++++++++++++++++ ...spiciousSectionFormattingInspectionTest.kt | 32 +++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt create mode 100644 test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt diff --git a/resources/META-INF/extensions/inspections/latex/probablebugs/probablebugs.xml b/resources/META-INF/extensions/inspections/latex/probablebugs/probablebugs.xml index 3456839f5..e7e978535 100644 --- a/resources/META-INF/extensions/inspections/latex/probablebugs/probablebugs.xml +++ b/resources/META-INF/extensions/inspections/latex/probablebugs/probablebugs.xml @@ -24,6 +24,10 @@ groupPath="LaTeX" groupName="Probable bugs" displayName="Unresolved references" enabledByDefault="true" level="WARNING" /> + { + return file.commandsInFile() + .filter { it.name in CommandMagic.sectionMarkers } + .filter { it.requiredParameter(0)?.containsAny(setOf("~", "\\\\")) == true } + .map { manager.createProblemDescriptor( + it, + "Suspicious formatting in ${it.name}", + GeneralMagic.noQuickFix, + ProblemHighlightType.WARNING, + isOntheFly + ) } + } +} \ No newline at end of file diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt new file mode 100644 index 000000000..dad198140 --- /dev/null +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt @@ -0,0 +1,32 @@ +package nl.hannahsten.texifyidea.inspections.latex.probablebugs + +import nl.hannahsten.texifyidea.file.LatexFileType +import nl.hannahsten.texifyidea.inspections.TexifyInspectionTestBase +import nl.hannahsten.texifyidea.util.magic.EnvironmentMagic + +internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspectionTestBase(LatexSuspiciousSectionFormattingInspection()) { + + fun `test ~ warning`() { + myFixture.configureByText( + LatexFileType, + "\\section{You should not use~in the title of a section}" + ) + myFixture.checkHighlighting(true, false, true, false) + } + +// fun `test ~ warning`() { +// myFixture.configureByText( +// LatexFileType, +// "\\section{You should not use~in the title of a section}" +// ) +// myFixture.checkHighlighting(true, false, true, false) +// } + + fun `test no warning when optional argument is present`() { + myFixture.configureByText( + LatexFileType, + "\\section[Table of contents long title]{Title with explicit \\\\ formatting}" + ) + myFixture.checkHighlighting(true, false, true, false) + } +} From 48c5474dc55ea906c22c21418260a957fb1e3ed9 Mon Sep 17 00:00:00 2001 From: Abby Berkers Date: Mon, 15 May 2023 21:39:19 +0200 Subject: [PATCH 02/10] Highlight suspicious formatting command instead of whole argument (TEX-72) --- ...exSuspiciousSectionFormattingInspection.kt | 32 ++++++++++++------- ...spiciousSectionFormattingInspectionTest.kt | 10 +----- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt index 371e72e7b..875665575 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt @@ -5,25 +5,24 @@ import com.intellij.codeInspection.ProblemDescriptor import com.intellij.codeInspection.ProblemHighlightType import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiFile +import com.intellij.webSymbols.references.WebSymbolReferenceProvider.Companion.startOffsetIn import nl.hannahsten.texifyidea.inspections.InsightGroup import nl.hannahsten.texifyidea.inspections.TexifyInspectionBase -import nl.hannahsten.texifyidea.lang.commands.LatexNewDefinitionCommand import nl.hannahsten.texifyidea.psi.LatexCommands +import nl.hannahsten.texifyidea.psi.LatexRequiredParam import nl.hannahsten.texifyidea.util.containsAny import nl.hannahsten.texifyidea.util.files.commandsInFile +import nl.hannahsten.texifyidea.util.firstChildOfType import nl.hannahsten.texifyidea.util.magic.CommandMagic -import nl.hannahsten.texifyidea.util.magic.GeneralMagic -import nl.hannahsten.texifyidea.util.magic.PatternMagic -import nl.hannahsten.texifyidea.util.matches -import nl.hannahsten.texifyidea.util.previousCommand import nl.hannahsten.texifyidea.util.requiredParameter -import java.util.* /** * @author Hannah Schellekens */ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { + private val formatting = setOf("~", "\\\\") + override val inspectionGroup = InsightGroup.LATEX override fun getDisplayName() = "Suspicious formatting in the required argument of a sectioning command" @@ -32,14 +31,25 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { override fun inspectFile(file: PsiFile, manager: InspectionManager, isOntheFly: Boolean): List { return file.commandsInFile() + .asSequence() .filter { it.name in CommandMagic.sectionMarkers } - .filter { it.requiredParameter(0)?.containsAny(setOf("~", "\\\\")) == true } - .map { manager.createProblemDescriptor( - it, - "Suspicious formatting in ${it.name}", - GeneralMagic.noQuickFix, + .filter { it.optionalParameterMap.isEmpty() } + .filter { it.requiredParameter(0)?.containsAny(formatting) == true } + .map { Pair(it, it.findTextRange() ?: TextRange(0, it.textLength)) } + .map { (psiElement, textRange) -> manager.createProblemDescriptor( + psiElement, + textRange, + "Suspicious formatting in ${psiElement.name}", ProblemHighlightType.WARNING, isOntheFly ) } + .toList() + } + + private fun LatexCommands.findTextRange(): TextRange? { + val (startOffset, text) = requiredParameter(0)?.findAnyOf(formatting) ?: return null + // Start offset of the required argument, plus 1 for the opening brace ({), plus the offset of the found string. + val startInParent = firstChildOfType(LatexRequiredParam::class)?.startOffsetIn(this)?.plus(1)?.plus(startOffset) ?: return null + return TextRange(startInParent, startInParent + text.length) } } \ No newline at end of file diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt index dad198140..4abbbc88e 100644 --- a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt @@ -9,19 +9,11 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection fun `test ~ warning`() { myFixture.configureByText( LatexFileType, - "\\section{You should not use~in the title of a section}" + "\\section{You should not use~in the title of a section}" ) myFixture.checkHighlighting(true, false, true, false) } -// fun `test ~ warning`() { -// myFixture.configureByText( -// LatexFileType, -// "\\section{You should not use~in the title of a section}" -// ) -// myFixture.checkHighlighting(true, false, true, false) -// } - fun `test no warning when optional argument is present`() { myFixture.configureByText( LatexFileType, From 87c72e32bcacff9df29d2c42937bc8b6b995e553 Mon Sep 17 00:00:00 2001 From: Abby Berkers Date: Mon, 15 May 2023 21:44:22 +0200 Subject: [PATCH 03/10] Add failing testcases --- ...spiciousSectionFormattingInspectionTest.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt index 4abbbc88e..63d247499 100644 --- a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt @@ -14,6 +14,22 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection myFixture.checkHighlighting(true, false, true, false) } + fun `test backslash warning`() { + myFixture.configureByText( + LatexFileType, + "\\section{You should not use\\\\in the title of a section}" + ) + myFixture.checkHighlighting(true, false, true, false) + } + + fun `test multiple warnings in one section`() { + myFixture.configureByText( + LatexFileType, + "\\section{You should not use~in the title~of a section}" + ) + myFixture.checkHighlighting(true, false, true, false) + } + fun `test no warning when optional argument is present`() { myFixture.configureByText( LatexFileType, @@ -21,4 +37,12 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection ) myFixture.checkHighlighting(true, false, true, false) } + + fun `test simple quickfix`() { + myFixture.configureByText( + LatexFileType, + "\\section{You should not use~in the title}" + ) + testQuickFix("\\section{You should not use~in the title}", "\\section[You should not use in the title]{You should not use~in the title}") + } } From bd4e2c855ab9b57d5f54c9c4d1837fd209bd6f61 Mon Sep 17 00:00:00 2001 From: Abby Berkers Date: Wed, 17 May 2023 21:57:01 +0200 Subject: [PATCH 04/10] Highlight multiple suspicious formatting items in single section title (TEX-72) --- ...exSuspiciousSectionFormattingInspection.kt | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt index 875665575..160bfca5d 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt @@ -16,9 +16,6 @@ import nl.hannahsten.texifyidea.util.firstChildOfType import nl.hannahsten.texifyidea.util.magic.CommandMagic import nl.hannahsten.texifyidea.util.requiredParameter -/** - * @author Hannah Schellekens - */ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { private val formatting = setOf("~", "\\\\") @@ -35,21 +32,36 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { .filter { it.name in CommandMagic.sectionMarkers } .filter { it.optionalParameterMap.isEmpty() } .filter { it.requiredParameter(0)?.containsAny(formatting) == true } - .map { Pair(it, it.findTextRange() ?: TextRange(0, it.textLength)) } - .map { (psiElement, textRange) -> manager.createProblemDescriptor( - psiElement, - textRange, - "Suspicious formatting in ${psiElement.name}", - ProblemHighlightType.WARNING, - isOntheFly - ) } + .map { Pair(it, it.findTextRanges()) } + .flatMap { (psiElement, textRanges) -> + textRanges.map { + manager.createProblemDescriptor( + psiElement, + it, + "Suspicious formatting in ${psiElement.name}", + ProblemHighlightType.WARNING, + isOntheFly + ) + } + } .toList() } - private fun LatexCommands.findTextRange(): TextRange? { - val (startOffset, text) = requiredParameter(0)?.findAnyOf(formatting) ?: return null - // Start offset of the required argument, plus 1 for the opening brace ({), plus the offset of the found string. - val startInParent = firstChildOfType(LatexRequiredParam::class)?.startOffsetIn(this)?.plus(1)?.plus(startOffset) ?: return null - return TextRange(startInParent, startInParent + text.length) + private fun LatexCommands.findTextRanges(): List { + // Start offset of the required argument, plus 1 for the opening brace ({) + val requiredParameterOffset = firstChildOfType(LatexRequiredParam::class)?.startOffsetIn(this)?.plus(1) + ?: return emptyList() + val ranges = mutableSetOf() + var offsetInParam = requiredParameterOffset + val requiredParamText = requiredParameter(0) ?: return emptyList() + while (offsetInParam < requiredParamText.length) { + requiredParamText.findAnyOf(formatting, startIndex = offsetInParam)?.let { (offset, text) -> + val start = requiredParameterOffset + offset + val end = start + text.length + ranges.add(TextRange(start, end)) + offsetInParam = end + } ?: break + } + return ranges.toList() } } \ No newline at end of file From a2cb3127a6471ed90049244162ee556e794b10c8 Mon Sep 17 00:00:00 2001 From: Abby Berkers Date: Thu, 18 May 2023 12:31:02 +0200 Subject: [PATCH 05/10] Add quickfix for suspicious formatting in section title (TEX-72) --- ...exSuspiciousSectionFormattingInspection.kt | 26 ++++++++++++++++--- .../texifyidea/psi/LatexPsiHelper.kt | 5 ++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt index 160bfca5d..b9c02f439 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt @@ -1,14 +1,17 @@ package nl.hannahsten.texifyidea.inspections.latex.probablebugs import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiFile import com.intellij.webSymbols.references.WebSymbolReferenceProvider.Companion.startOffsetIn import nl.hannahsten.texifyidea.inspections.InsightGroup import nl.hannahsten.texifyidea.inspections.TexifyInspectionBase import nl.hannahsten.texifyidea.psi.LatexCommands +import nl.hannahsten.texifyidea.psi.LatexPsiHelper import nl.hannahsten.texifyidea.psi.LatexRequiredParam import nl.hannahsten.texifyidea.util.containsAny import nl.hannahsten.texifyidea.util.files.commandsInFile @@ -18,8 +21,6 @@ import nl.hannahsten.texifyidea.util.requiredParameter open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { - private val formatting = setOf("~", "\\\\") - override val inspectionGroup = InsightGroup.LATEX override fun getDisplayName() = "Suspicious formatting in the required argument of a sectioning command" @@ -40,7 +41,8 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { it, "Suspicious formatting in ${psiElement.name}", ProblemHighlightType.WARNING, - isOntheFly + isOntheFly, + AddOptionalArgumentQuickFix() ) } } @@ -64,4 +66,22 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { } return ranges.toList() } + + class AddOptionalArgumentQuickFix : LocalQuickFix { + override fun getFamilyName(): String { + return "Add optional argument without formatting" + } + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val command = descriptor.psiElement as LatexCommands + val requiredParamText = command.requiredParameter(0) + val optionalParamText = requiredParamText?.replace(Regex("""~|\\"""), " ") ?: return + val optionalArgument = LatexPsiHelper(project).createOptionalParameter(optionalParamText) + command.addAfter(optionalArgument, command.commandToken) + } + } + + companion object { + val formatting = setOf("~", "\\\\") + } } \ No newline at end of file diff --git a/src/nl/hannahsten/texifyidea/psi/LatexPsiHelper.kt b/src/nl/hannahsten/texifyidea/psi/LatexPsiHelper.kt index 73e0c35a6..a54753263 100644 --- a/src/nl/hannahsten/texifyidea/psi/LatexPsiHelper.kt +++ b/src/nl/hannahsten/texifyidea/psi/LatexPsiHelper.kt @@ -84,6 +84,11 @@ class LatexPsiHelper(private val project: Project) { return createFromText(commandText).firstChildOfType(LatexRequiredParam::class)!! } + fun createOptionalParameter(content: String): LatexOptionalParam { + val commandText = "\\section[$content]{$content}" + return createFromText(commandText).firstChildOfType(LatexOptionalParam::class)!! + } + /** * Returns the LatexOptionalParam node that is supposed to contain the label key for the command. * If no such node exists yet, a new one is created at the correct position. From a3452d6d78a74c881f57bd432e414d295bebe549 Mon Sep 17 00:00:00 2001 From: Abby Berkers Date: Thu, 18 May 2023 17:30:32 +0200 Subject: [PATCH 06/10] Add description for suspicious section formatting inspection --- .../LatexSuspiciousSectionFormatting.html | 12 ++++++++++++ ...exSuspiciousSectionFormattingInspection.kt | 6 +++--- ...spiciousSectionFormattingInspectionTest.kt | 19 ++++++++++++++++++- 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html diff --git a/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html b/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html new file mode 100644 index 000000000..9fe553e11 --- /dev/null +++ b/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html @@ -0,0 +1,12 @@ + + +Reports usages of ~ and \\ in a Section-like command and suggests to add an optional argument to the command. +

+ This inspection is based on the following paragraph from The LaTeX Companion (2nd edition, page 23): + + If you try + +

+ + + \ No newline at end of file diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt index b9c02f439..add842a21 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt @@ -25,7 +25,7 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { override fun getDisplayName() = "Suspicious formatting in the required argument of a sectioning command" - override val inspectionId = "SuspiciousFormatSection" + override val inspectionId = "SuspiciousSectionFormatting" override fun inspectFile(file: PsiFile, manager: InspectionManager, isOntheFly: Boolean): List { return file.commandsInFile() @@ -54,7 +54,7 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { val requiredParameterOffset = firstChildOfType(LatexRequiredParam::class)?.startOffsetIn(this)?.plus(1) ?: return emptyList() val ranges = mutableSetOf() - var offsetInParam = requiredParameterOffset + var offsetInParam = 0 val requiredParamText = requiredParameter(0) ?: return emptyList() while (offsetInParam < requiredParamText.length) { requiredParamText.findAnyOf(formatting, startIndex = offsetInParam)?.let { (offset, text) -> @@ -75,7 +75,7 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val command = descriptor.psiElement as LatexCommands val requiredParamText = command.requiredParameter(0) - val optionalParamText = requiredParamText?.replace(Regex("""~|\\"""), " ") ?: return + val optionalParamText = requiredParamText?.replace(Regex(formatting.joinToString("", prefix = "[", postfix = "]")), " ") ?: return val optionalArgument = LatexPsiHelper(project).createOptionalParameter(optionalParamText) command.addAfter(optionalArgument, command.commandToken) } diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt index 63d247499..9fdb8f32d 100644 --- a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt @@ -14,6 +14,14 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection myFixture.checkHighlighting(true, false, true, false) } + fun `test ~ warning for short section`() { + myFixture.configureByText( + LatexFileType, + "\\section{a~b}" + ) + myFixture.checkHighlighting(true, false, true, false) + } + fun `test backslash warning`() { myFixture.configureByText( LatexFileType, @@ -30,7 +38,15 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection myFixture.checkHighlighting(true, false, true, false) } - fun `test no warning when optional argument is present`() { + fun `test no warning for ~ when optional argument is present`() { + myFixture.configureByText( + LatexFileType, + "\\section[Table of contents long title]{Title with explicit~formatting}" + ) + myFixture.checkHighlighting(true, false, true, false) + } + + fun `test no warning for backslash when optional argument is present`() { myFixture.configureByText( LatexFileType, "\\section[Table of contents long title]{Title with explicit \\\\ formatting}" @@ -44,5 +60,6 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection "\\section{You should not use~in the title}" ) testQuickFix("\\section{You should not use~in the title}", "\\section[You should not use in the title]{You should not use~in the title}") + myFixture.checkHighlighting(true, false, true, false) } } From 4581e3fae5d0d247c484ee5b021d956a6c3ab326 Mon Sep 17 00:00:00 2001 From: Abby Berkers Date: Fri, 19 May 2023 21:29:32 +0200 Subject: [PATCH 07/10] Fix bug with finding text ranges and showing warning after fix had been applied (TEX-72) --- .../LatexSuspiciousSectionFormatting.html | 7 ++++--- ...exSuspiciousSectionFormattingInspection.kt | 7 ++++++- ...spiciousSectionFormattingInspectionTest.kt | 19 ++++++++++++++++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html b/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html index 9fe553e11..45534f9c6 100644 --- a/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html +++ b/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html @@ -3,9 +3,10 @@ Reports usages of ~ and \\ in a Section-like command and suggests to add an optional argument to the command.

This inspection is based on the following paragraph from The LaTeX Companion (2nd edition, page 23): - - If you try - +

+ If you try to advise TeX on how to split the heading over a few lines using the '~' symbol so the '\\' command, then side effects may result when formatting the table of contents or generating the running head. + In this case the simplest solution is to repeat the heading text without the specific markup in the optional parameter of the sectioning command. +

diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt index add842a21..102586c1d 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt @@ -61,7 +61,7 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { val start = requiredParameterOffset + offset val end = start + text.length ranges.add(TextRange(start, end)) - offsetInParam = end + offsetInParam = offset + text.length } ?: break } return ranges.toList() @@ -77,7 +77,12 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { val requiredParamText = command.requiredParameter(0) val optionalParamText = requiredParamText?.replace(Regex(formatting.joinToString("", prefix = "[", postfix = "]")), " ") ?: return val optionalArgument = LatexPsiHelper(project).createOptionalParameter(optionalParamText) + command.addAfter(optionalArgument, command.commandToken) + // Create a new command and completely replace the old command so all the psi methods will recompute instead + // of using old values from their cache. + val newCommand = LatexPsiHelper(project).createFromText(command.text).firstChildOfType(LatexCommands::class) ?: return + command.parent.replace(newCommand) } } diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt index 9fdb8f32d..d1aa232b5 100644 --- a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt @@ -38,6 +38,14 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection myFixture.checkHighlighting(true, false, true, false) } + fun `test multiple ~ warnings in one section`() { + myFixture.configureByText( + LatexFileType, + "\\section{test~no space~spacing}" + ) + myFixture.checkHighlighting(true, false, true, false) + } + fun `test no warning for ~ when optional argument is present`() { myFixture.configureByText( LatexFileType, @@ -54,7 +62,7 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection myFixture.checkHighlighting(true, false, true, false) } - fun `test simple quickfix`() { + fun `test simple quickfix for ~`() { myFixture.configureByText( LatexFileType, "\\section{You should not use~in the title}" @@ -62,4 +70,13 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection testQuickFix("\\section{You should not use~in the title}", "\\section[You should not use in the title]{You should not use~in the title}") myFixture.checkHighlighting(true, false, true, false) } + + fun `test simple quickfix for backslash`() { + myFixture.configureByText( + LatexFileType, + "\\section{You should not use~in the title}" + ) + testQuickFix("\\section{You should not use \\\\ in the title}", "\\section[You should not use in the title]{You should not use \\\\ in the title}") + myFixture.checkHighlighting(true, false, true, false) + } } From 3adeee441ec01f52ee47f323f363c88abbc9e402 Mon Sep 17 00:00:00 2001 From: Abby Berkers Date: Fri, 19 May 2023 22:11:56 +0200 Subject: [PATCH 08/10] Highlight required parameter of section command when it contains suspicious formatting (TEX-72) --- ...exSuspiciousSectionFormattingInspection.kt | 52 +++++++------------ ...spiciousSectionFormattingInspectionTest.kt | 23 +++----- 2 files changed, 27 insertions(+), 48 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt index 102586c1d..8cda973d1 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt @@ -33,41 +33,26 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { .filter { it.name in CommandMagic.sectionMarkers } .filter { it.optionalParameterMap.isEmpty() } .filter { it.requiredParameter(0)?.containsAny(formatting) == true } - .map { Pair(it, it.findTextRanges()) } - .flatMap { (psiElement, textRanges) -> - textRanges.map { - manager.createProblemDescriptor( - psiElement, - it, - "Suspicious formatting in ${psiElement.name}", - ProblemHighlightType.WARNING, - isOntheFly, - AddOptionalArgumentQuickFix() - ) - } + .map { psiElement -> + val requiredParam = psiElement.firstChildOfType(LatexRequiredParam::class) + // Plus 1 for the opening brace. + val startOffset = requiredParam?.startOffsetIn(psiElement)?.plus(1) ?: 0 + // Minus 2 for the braces surrounding the parameter. + val endOffset = requiredParam?.textLength?.minus(2)?.plus(startOffset) ?: psiElement.textLength + manager.createProblemDescriptor( + psiElement, + TextRange(startOffset, endOffset), + "Suspicious formatting in ${psiElement.name}", + ProblemHighlightType.WARNING, + isOntheFly, + AddOptionalArgumentQuickFix() + ) } .toList() } - private fun LatexCommands.findTextRanges(): List { - // Start offset of the required argument, plus 1 for the opening brace ({) - val requiredParameterOffset = firstChildOfType(LatexRequiredParam::class)?.startOffsetIn(this)?.plus(1) - ?: return emptyList() - val ranges = mutableSetOf() - var offsetInParam = 0 - val requiredParamText = requiredParameter(0) ?: return emptyList() - while (offsetInParam < requiredParamText.length) { - requiredParamText.findAnyOf(formatting, startIndex = offsetInParam)?.let { (offset, text) -> - val start = requiredParameterOffset + offset - val end = start + text.length - ranges.add(TextRange(start, end)) - offsetInParam = offset + text.length - } ?: break - } - return ranges.toList() - } - class AddOptionalArgumentQuickFix : LocalQuickFix { + override fun getFamilyName(): String { return "Add optional argument without formatting" } @@ -75,18 +60,21 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val command = descriptor.psiElement as LatexCommands val requiredParamText = command.requiredParameter(0) - val optionalParamText = requiredParamText?.replace(Regex(formatting.joinToString("", prefix = "[", postfix = "]")), " ") ?: return + val optionalParamText = requiredParamText?.replace(Regex(formatting.joinToString("", prefix = "[", postfix = "]")), " ") + ?: return val optionalArgument = LatexPsiHelper(project).createOptionalParameter(optionalParamText) command.addAfter(optionalArgument, command.commandToken) // Create a new command and completely replace the old command so all the psi methods will recompute instead // of using old values from their cache. - val newCommand = LatexPsiHelper(project).createFromText(command.text).firstChildOfType(LatexCommands::class) ?: return + val newCommand = LatexPsiHelper(project).createFromText(command.text).firstChildOfType(LatexCommands::class) + ?: return command.parent.replace(newCommand) } } companion object { + val formatting = setOf("~", "\\\\") } } \ No newline at end of file diff --git a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt index d1aa232b5..e5fc4c733 100644 --- a/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt +++ b/test/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspectionTest.kt @@ -2,22 +2,21 @@ package nl.hannahsten.texifyidea.inspections.latex.probablebugs import nl.hannahsten.texifyidea.file.LatexFileType import nl.hannahsten.texifyidea.inspections.TexifyInspectionTestBase -import nl.hannahsten.texifyidea.util.magic.EnvironmentMagic internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspectionTestBase(LatexSuspiciousSectionFormattingInspection()) { fun `test ~ warning`() { myFixture.configureByText( LatexFileType, - "\\section{You should not use~in the title of a section}" + "\\section{You should not use~in the title of a section}" ) myFixture.checkHighlighting(true, false, true, false) } fun `test ~ warning for short section`() { myFixture.configureByText( - LatexFileType, - "\\section{a~b}" + LatexFileType, + "\\section{a~b}" ) myFixture.checkHighlighting(true, false, true, false) } @@ -25,7 +24,7 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection fun `test backslash warning`() { myFixture.configureByText( LatexFileType, - "\\section{You should not use\\\\in the title of a section}" + "\\section{You should not use\\\\in the title of a section}" ) myFixture.checkHighlighting(true, false, true, false) } @@ -33,23 +32,15 @@ internal class LatexSuspiciousSectionFormattingInspectionTest : TexifyInspection fun `test multiple warnings in one section`() { myFixture.configureByText( LatexFileType, - "\\section{You should not use~in the title~of a section}" - ) - myFixture.checkHighlighting(true, false, true, false) - } - - fun `test multiple ~ warnings in one section`() { - myFixture.configureByText( - LatexFileType, - "\\section{test~no space~spacing}" + "\\section{You should not use~in the title~of a section}" ) myFixture.checkHighlighting(true, false, true, false) } fun `test no warning for ~ when optional argument is present`() { myFixture.configureByText( - LatexFileType, - "\\section[Table of contents long title]{Title with explicit~formatting}" + LatexFileType, + "\\section[Table of contents long title]{Title with explicit~formatting}" ) myFixture.checkHighlighting(true, false, true, false) } From 0a66c72637c5f9fbf3b6459cd86530c3247fea1a Mon Sep 17 00:00:00 2001 From: = Date: Sat, 27 May 2023 09:36:26 +0200 Subject: [PATCH 09/10] Remove nested html paragraph --- .../LatexSuspiciousSectionFormatting.html | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html b/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html index 45534f9c6..66c224281 100644 --- a/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html +++ b/resources/inspectionDescriptions/LatexSuspiciousSectionFormatting.html @@ -1,13 +1,14 @@ -Reports usages of ~ and \\ in a Section-like command and suggests to add an optional argument to the command. +Reports usages of ~ and \\ in a Section-like command and suggests to add an optional argument to the +command.

This inspection is based on the following paragraph from The LaTeX Companion (2nd edition, page 23): -

- If you try to advise TeX on how to split the heading over a few lines using the '~' symbol so the '\\' command, then side effects may result when formatting the table of contents or generating the running head. - In this case the simplest solution is to repeat the heading text without the specific markup in the optional parameter of the sectioning command. -

-

- +

+ If you try to advise TeX on how to split the heading over a few lines using the '~' symbol so the '\\' command, then + side effects may result when formatting the table of contents or generating the running head. + In this case the simplest solution is to repeat the heading text without the specific markup in the optional + parameter of the sectioning command. + \ No newline at end of file From 72a7b3e4388d521e9fc0c24196b5482dc16826ba Mon Sep 17 00:00:00 2001 From: = Date: Sat, 27 May 2023 09:41:22 +0200 Subject: [PATCH 10/10] Fix PR comments --- .../LatexSuspiciousSectionFormattingInspection.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt index 8cda973d1..5719a44c4 100644 --- a/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt +++ b/src/nl/hannahsten/texifyidea/inspections/latex/probablebugs/LatexSuspiciousSectionFormattingInspection.kt @@ -54,7 +54,7 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { class AddOptionalArgumentQuickFix : LocalQuickFix { override fun getFamilyName(): String { - return "Add optional argument without formatting" + return "Fix formatting in table of contents and running head" } override fun applyFix(project: Project, descriptor: ProblemDescriptor) { @@ -69,7 +69,7 @@ open class LatexSuspiciousSectionFormattingInspection : TexifyInspectionBase() { // of using old values from their cache. val newCommand = LatexPsiHelper(project).createFromText(command.text).firstChildOfType(LatexCommands::class) ?: return - command.parent.replace(newCommand) + command.replace(newCommand) } }