1
1
package nl.hannahsten.texifyidea.util
2
2
3
+ import com.intellij.lang.ASTNode
3
4
import com.intellij.openapi.project.Project
4
5
import com.intellij.openapi.util.text.StringUtil
5
6
import com.intellij.psi.PsiDocumentManager
6
7
import com.intellij.psi.PsiElement
7
8
import com.intellij.psi.PsiFile
9
+ import com.intellij.psi.impl.source.tree.TreeUtil
8
10
import nl.hannahsten.texifyidea.index.file.LatexExternalPackageInclusionCache
9
11
import nl.hannahsten.texifyidea.lang.LatexPackage
10
12
import nl.hannahsten.texifyidea.lang.commands.LatexGenericRegularCommand
@@ -14,6 +16,7 @@ import nl.hannahsten.texifyidea.settings.TexifySettings
14
16
import nl.hannahsten.texifyidea.util.files.*
15
17
import nl.hannahsten.texifyidea.util.magic.CommandMagic
16
18
import nl.hannahsten.texifyidea.util.magic.PackageMagic
19
+ import nl.hannahsten.texifyidea.util.magic.cmd
17
20
import nl.hannahsten.texifyidea.util.parser.firstParentOfType
18
21
import nl.hannahsten.texifyidea.util.parser.toStringMap
19
22
@@ -34,6 +37,38 @@ object PackageUtils {
34
37
?.split(" ;" )
35
38
?.toList() ? : emptyList()
36
39
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
+
37
72
/* *
38
73
* Inserts a usepackage statement for the given package in a certain file.
39
74
*
@@ -69,40 +104,29 @@ object PackageUtils {
69
104
}
70
105
}
71
106
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)
99
108
100
109
var command = commandName
101
110
command + = if (parameters == null || " " == parameters) " " else " [$parameters ]"
102
111
command + = " {$packageName }"
103
112
104
113
val newNode = LatexPsiHelper (file.project).createFromText(command).firstChild.node
105
114
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
+ ) {
106
130
// Don't run in a write action, as that will produce a SideEffectsNotAllowedException for INVOKE_LATER
107
131
108
132
// Avoid "Attempt to modify PSI for non-committed Document"
@@ -111,18 +135,21 @@ object PackageUtils {
111
135
.doPostponedOperationsAndUnblockDocument(file.document() ? : return )
112
136
PsiDocumentManager .getInstance(file.project).commitDocument(file.document() ? : return )
113
137
runWriteAction {
138
+ val newlineText = if (prependBlankLine) " \n\n " else " \n "
139
+ val newLine = LatexPsiHelper (file.project).createFromText(newlineText).firstChild.node
114
140
// 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 ) {
116
142
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) {
120
144
anchorAfter.parent.node.addChild(newLine, anchorBefore)
121
145
}
122
146
anchorAfter.parent.node.addChild(newNode, anchorBefore)
123
147
}
124
148
else {
125
149
// Insert at beginning
150
+ if (prependNewLine || prependBlankLine) {
151
+ file.node.addChild(newLine, file.firstChild.node)
152
+ }
126
153
file.node.addChild(newNode, file.firstChild.node)
127
154
}
128
155
}
0 commit comments