Repository: hannah-sten/texify-idea
Issue: "Environment requires amsmath package" error when using certain environments even while amsmath is included

#### Type of JetBrains IDE (IntelliJ, PyCharm, etc.) and version
IntelliJ Idea Ultimate 2019.3.3

#### Operating System 
<!-- Windows, Ubuntu, Arch Linux, MacOS, etc. -->
Windows 10 Pro 1909

#### TeXiFy IDEA version
0.6.8

#### What I did (steps to reproduce)
Create new .tex file using one of "equation*", "cases" and "flalign*":
```
\\documentclass[11pt]{article}
\\usepackage{amsmath}
\\begin{document}
    \\begin{equation*}
         2
    \\end{equation*}
\\end{document}
```

#### Expected behavior
There should be no errors, as the document compiles perfectly with pdfLaTeX.

#### Actual behavior
The environment name in the opener (line 4) is highlighted, with the popup "Environment requires amsmath package". When the "Add import for package 'amsmath'" option of the popup is clicked, the packaged is re-included at the very top of the file, but the error doesn't disappear. Successive clicks create even more "\\usepackage{amsmath}" lines, to no avail. While "equation*" gives an error, strangely, "equation" doesn't. There are likely more amsmath-environments that give the error than the ones I found.

Thank you for the fantastic plugin!

Changed files:
src/nl/hannahsten/texifyidea/util/Packages.kt

diff --git a/src/nl/hannahsten/texifyidea/util/Packages.kt b/src/nl/hannahsten/texifyidea/util/Packages.kt
index 31a84daa1..75ca108cc 100644
--- a/src/nl/hannahsten/texifyidea/util/Packages.kt
+++ b/src/nl/hannahsten/texifyidea/util/Packages.kt
@@ -1,10 +1,11 @@
 package nl.hannahsten.texifyidea.util
 
-import com.intellij.openapi.editor.Document
 import com.intellij.openapi.util.text.StringUtil
+import com.intellij.psi.PsiElement
 import com.intellij.psi.PsiFile
 import nl.hannahsten.texifyidea.lang.Package
 import nl.hannahsten.texifyidea.psi.LatexCommands
+import nl.hannahsten.texifyidea.psi.LatexPsiHelper
 import nl.hannahsten.texifyidea.settings.TexifySettings
 import nl.hannahsten.texifyidea.util.files.*
 
@@ -41,7 +42,7 @@ object PackageUtils {
      *          Parameters to add to the statement, `null` or empty string for no parameters.
      */
     @JvmStatic
-    fun insertUsepackage(document: Document, file: PsiFile, packageName: String, parameters: String?) {
+    fun insertUsepackage(file: PsiFile, packageName: String, parameters: String?) {
 
         if (!TexifySettings.getInstance().automaticDependencyCheck) {
             return
@@ -64,9 +65,9 @@ object PackageUtils {
             }
         }
 
-        val newlines: String
-        val insertLocation: Int
-        var postNewlines: String? = null
+        val prependNewLine: Boolean
+        // The anchor after which the new element will be inserted
+        val anchorAfter: PsiElement?
 
         // When there are no usepackage commands: insert below documentclass.
         if (last == null) {
@@ -77,32 +78,39 @@ object PackageUtils {
                     }
                     .firstOrNull()
             if (classHuh != null) {
-                insertLocation = classHuh.textOffset + classHuh.textLength
-                newlines = "\\n"
+                anchorAfter = classHuh
+                prependNewLine = true
             }
             else {
                 // No other sensible location can be found
-                insertLocation = 0
-                newlines = ""
-                postNewlines = "\\n\\n"
+                anchorAfter = null
+                prependNewLine = false
             }
 
         }
         // Otherwise, insert below the lowest usepackage.
         else {
-            insertLocation = last.textOffset + last.textLength
-            newlines = "\\n"
+            anchorAfter = last
+            prependNewLine = true
         }
 
-        var command = newlines + commandName
+        var command = commandName
         command += if (parameters == null || "" == parameters) "" else "[$parameters]"
         command += "{$packageName}"
 
-        if (postNewlines != null) {
-            command += postNewlines
+        val newNode = LatexPsiHelper(file.project).createFromText(command).firstChild.node
+        if (anchorAfter != null) {
+            val anchorBefore = anchorAfter.node.treeNext
+            if (prependNewLine) {
+                val newLine = LatexPsiHelper(file.project).createFromText("\\n").firstChild.node
+                anchorAfter.parent.node.addChild(newLine, anchorBefore)
+            }
+            anchorAfter.parent.node.addChild(newNode, anchorBefore)
+        }
+        else {
+            // Insert at beginning
+            file.node.addChild(newNode, file.firstChild.node)
         }
-
-        document.insertString(insertLocation, command)
     }
 
     /**
@@ -139,11 +147,9 @@ object PackageUtils {
         // Packages should always be included in the root file
         val rootFile = file.findRootFile()
 
-        val document = rootFile.document() ?: return true
-
         val params = pack.parameters
         val parameterString = StringUtil.join(params, ",")
-        insertUsepackage(document, rootFile, pack.name, parameterString)
+        insertUsepackage(rootFile, pack.name, parameterString)
 
         return true
     }
@@ -192,28 +198,6 @@ object PackageUtils {
         return getIncludedPackages(commands, ArrayList())
     }
 
-    /**
-     * Analyses the given file and finds all packages included in that file only (not the file set!)
-     *
-     * @return A set containing all used packages in the given file.
-     */
-    @JvmStatic
-    fun getIncludedPackagesOfSingleFile(baseFile: PsiFile): Set<String> {
-        val commands = baseFile.commandsInFile()
-        return getIncludedPackages(commands, HashSet())
-    }
-
-    /**
-     * Analyses the given file and finds all packages included in that file only (not the file set!)
-     *
-     * @return A list containing all used package names (including duplicates).
-     */
-    @JvmStatic
-    fun getIncludedPackagesOfSingleFileList(baseFile: PsiFile): List<String> {
-        val commands = baseFile.commandsInFile()
-        return getIncludedPackages(commands, ArrayList())
-    }
-
     /**
      * Gets all packages imported with [PACKAGE_COMMANDS].
      */