Skip to content

Commit a4dade0

Browse files
committed
Reduce code duplication
1 parent caf0ed5 commit a4dade0

File tree

2 files changed

+64
-79
lines changed

2 files changed

+64
-79
lines changed

src/nl/hannahsten/texifyidea/util/Commands.kt

Lines changed: 6 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package nl.hannahsten.texifyidea.util
22

33
import com.intellij.openapi.project.Project
4-
import com.intellij.psi.PsiDocumentManager
54
import com.intellij.psi.PsiElement
65
import com.intellij.psi.PsiFile
76
import com.intellij.psi.search.GlobalSearchScope
@@ -13,6 +12,7 @@ import nl.hannahsten.texifyidea.lang.commands.RequiredFileArgument
1312
import nl.hannahsten.texifyidea.psi.LatexCommands
1413
import nl.hannahsten.texifyidea.psi.LatexParameter
1514
import nl.hannahsten.texifyidea.psi.LatexPsiHelper
15+
import nl.hannahsten.texifyidea.util.PackageUtils.getDefaultInsertAnchor
1616
import nl.hannahsten.texifyidea.util.files.*
1717
import nl.hannahsten.texifyidea.util.labels.getLabelDefinitionCommands
1818
import nl.hannahsten.texifyidea.util.magic.CommandMagic
@@ -70,30 +70,6 @@ fun insertCommandDefinition(file: PsiFile, commandText: String, newCommandName:
7070
}
7171
}
7272

73-
// The anchor after which the new element will be inserted
74-
val anchorAfter: PsiElement?
75-
76-
// When there are no usepackage commands: insert below documentclass.
77-
if (last == null) {
78-
val classHuh = commands.asSequence()
79-
.filter { cmd ->
80-
"\\documentclass" == cmd.commandToken
81-
.text || "\\LoadClass" == cmd.commandToken.text
82-
}
83-
.firstOrNull()
84-
if (classHuh != null) {
85-
anchorAfter = classHuh
86-
}
87-
else {
88-
// No other sensible location can be found
89-
anchorAfter = null
90-
}
91-
}
92-
// Otherwise, insert below the lowest usepackage.
93-
else {
94-
anchorAfter = last
95-
}
96-
9773
val blockingNames = file.definitions().filter { it.commandToken.text.matches("${newCommandName}\\d*".toRegex()) }
9874

9975
val nonConflictingName = "${newCommandName}${if (blockingNames.isEmpty()) "" else blockingNames.size.toString()}"
@@ -102,29 +78,11 @@ fun insertCommandDefinition(file: PsiFile, commandText: String, newCommandName:
10278
val newChild = LatexPsiHelper(file.project).createFromText(command).firstChild
10379
val newNode = newChild.node
10480

105-
// Don't run in a write action, as that will produce a SideEffectsNotAllowedException for INVOKE_LATER
106-
107-
// Avoid "Attempt to modify PSI for non-committed Document"
108-
// https://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/modifying_psi.html?search=refac#combining-psi-and-document-modifications
109-
PsiDocumentManager.getInstance(file.project)
110-
.doPostponedOperationsAndUnblockDocument(file.document() ?: return null)
111-
PsiDocumentManager.getInstance(file.project).commitDocument(file.document() ?: return null)
112-
113-
runWriteAction {
114-
val newLine = LatexPsiHelper(file.project).createFromText("\n\n").firstChild.node
115-
// Avoid NPE, see #3083 (cause unknown)
116-
if (anchorAfter != null && com.intellij.psi.impl.source.tree.TreeUtil.getFileElement(anchorAfter.parent.node) != null) {
117-
val anchorBefore = anchorAfter.node.treeNext
118-
anchorAfter.parent.node.addChild(newLine, anchorBefore)
119-
anchorAfter.parent.node.addChild(newNode, anchorBefore)
120-
}
121-
else {
122-
// Insert at beginning
123-
file.node.addChild(newLine, file.firstChild.node)
124-
file.node.addChild(newNode, file.firstChild.node)
125-
// file.node.addChild(newLine, file.firstChild.node)
126-
}
127-
}
81+
// The anchor after which the new element will be inserted
82+
// When there are no usepackage commands: insert below documentclass.
83+
val (anchorAfter, _) = getDefaultInsertAnchor(commands, last)
84+
85+
PackageUtils.insertNodeAfterAnchor(file, anchorAfter, prependNewLine = true, newNode, prependBlankLine = true)
12886

12987
return newChild
13088
}

src/nl/hannahsten/texifyidea/util/Packages.kt

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package nl.hannahsten.texifyidea.util
22

3+
import com.intellij.lang.ASTNode
34
import com.intellij.openapi.project.Project
45
import com.intellij.openapi.util.text.StringUtil
56
import com.intellij.psi.PsiDocumentManager
67
import com.intellij.psi.PsiElement
78
import com.intellij.psi.PsiFile
9+
import com.intellij.psi.impl.source.tree.TreeUtil
810
import nl.hannahsten.texifyidea.index.file.LatexExternalPackageInclusionCache
911
import nl.hannahsten.texifyidea.lang.LatexPackage
1012
import nl.hannahsten.texifyidea.lang.commands.LatexGenericRegularCommand
@@ -14,6 +16,7 @@ import nl.hannahsten.texifyidea.settings.TexifySettings
1416
import nl.hannahsten.texifyidea.util.files.*
1517
import nl.hannahsten.texifyidea.util.magic.CommandMagic
1618
import nl.hannahsten.texifyidea.util.magic.PackageMagic
19+
import nl.hannahsten.texifyidea.util.magic.cmd
1720
import nl.hannahsten.texifyidea.util.parser.firstParentOfType
1821
import nl.hannahsten.texifyidea.util.parser.toStringMap
1922

@@ -34,6 +37,38 @@ object PackageUtils {
3437
?.split(";")
3538
?.toList() ?: emptyList()
3639

40+
/**
41+
* Get the default psi element to insert new packages/definitions after.
42+
* The anchor will be the given preferred anchor if not null.
43+
*/
44+
fun getDefaultInsertAnchor(commands: Collection<LatexCommands>, preferredAnchor: LatexCommands?): Pair<PsiElement?, Boolean> {
45+
val classHuh = commands.asSequence()
46+
.filter { cmd ->
47+
cmd.name == LatexGenericRegularCommand.DOCUMENTCLASS.cmd || cmd.name == LatexGenericRegularCommand.LOADCLASS.cmd
48+
}
49+
.firstOrNull()
50+
val anchorAfter: PsiElement?
51+
val prependNewLine: Boolean
52+
if (classHuh != null) {
53+
anchorAfter = classHuh
54+
prependNewLine = true
55+
}
56+
else {
57+
// No other sensible location can be found
58+
anchorAfter = null
59+
prependNewLine = false
60+
}
61+
62+
return if (preferredAnchor == null) {
63+
Pair(anchorAfter, prependNewLine)
64+
}
65+
else {
66+
Pair(preferredAnchor, true)
67+
}
68+
}
69+
70+
71+
3772
/**
3873
* Inserts a usepackage statement for the given package in a certain file.
3974
*
@@ -69,40 +104,29 @@ object PackageUtils {
69104
}
70105
}
71106

72-
val prependNewLine: Boolean
73-
// The anchor after which the new element will be inserted
74-
val anchorAfter: PsiElement?
75-
76-
// When there are no usepackage commands: insert below documentclass.
77-
if (last == null) {
78-
val classHuh = commands.asSequence()
79-
.filter { cmd ->
80-
"\\documentclass" == cmd.commandToken
81-
.text || "\\LoadClass" == cmd.commandToken.text
82-
}
83-
.firstOrNull()
84-
if (classHuh != null) {
85-
anchorAfter = classHuh
86-
prependNewLine = true
87-
}
88-
else {
89-
// No other sensible location can be found
90-
anchorAfter = null
91-
prependNewLine = false
92-
}
93-
}
94-
// Otherwise, insert below the lowest usepackage.
95-
else {
96-
anchorAfter = last
97-
prependNewLine = true
98-
}
107+
val (anchorAfter, prependNewLine) = getDefaultInsertAnchor(commands, last)
99108

100109
var command = commandName
101110
command += if (parameters == null || "" == parameters) "" else "[$parameters]"
102111
command += "{$packageName}"
103112

104113
val newNode = LatexPsiHelper(file.project).createFromText(command).firstChild.node
105114

115+
insertNodeAfterAnchor(file, anchorAfter, prependNewLine, newNode)
116+
}
117+
118+
/**
119+
* Insert an AST node after a certain anchor, possibly with a newline.
120+
*
121+
* @param prependBlankLine If prependNewLine is true, you can set this to true to insert an additional blank line.
122+
*/
123+
fun insertNodeAfterAnchor(
124+
file: PsiFile,
125+
anchorAfter: PsiElement?,
126+
prependNewLine: Boolean,
127+
newNode: ASTNode,
128+
prependBlankLine: Boolean = false
129+
) {
106130
// Don't run in a write action, as that will produce a SideEffectsNotAllowedException for INVOKE_LATER
107131

108132
// Avoid "Attempt to modify PSI for non-committed Document"
@@ -111,18 +135,21 @@ object PackageUtils {
111135
.doPostponedOperationsAndUnblockDocument(file.document() ?: return)
112136
PsiDocumentManager.getInstance(file.project).commitDocument(file.document() ?: return)
113137
runWriteAction {
138+
val newlineText = if (prependBlankLine) "\n\n" else "\n"
139+
val newLine = LatexPsiHelper(file.project).createFromText(newlineText).firstChild.node
114140
// Avoid NPE, see #3083 (cause unknown)
115-
if (anchorAfter != null && com.intellij.psi.impl.source.tree.TreeUtil.getFileElement(anchorAfter.parent.node) != null) {
141+
if (anchorAfter != null && TreeUtil.getFileElement(anchorAfter.parent.node) != null) {
116142
val anchorBefore = anchorAfter.node.treeNext
117-
@Suppress("KotlinConstantConditions")
118-
if (prependNewLine) {
119-
val newLine = LatexPsiHelper(file.project).createFromText("\n").firstChild.node
143+
if (prependNewLine || prependBlankLine) {
120144
anchorAfter.parent.node.addChild(newLine, anchorBefore)
121145
}
122146
anchorAfter.parent.node.addChild(newNode, anchorBefore)
123147
}
124148
else {
125149
// Insert at beginning
150+
if (prependNewLine || prependBlankLine) {
151+
file.node.addChild(newLine, file.firstChild.node)
152+
}
126153
file.node.addChild(newNode, file.firstChild.node)
127154
}
128155
}

0 commit comments

Comments
 (0)