intellij плагин sdk - intellij.ui.tree.AsyncTreeModel исключение "дублированные дети" - PullRequest
0 голосов
/ 26 июня 2019

Я пишу плагин для IntelliJ IDE, который работает с деревом файлов; В частности, разваливающиеся и гнездящиеся узлы. Пока он отлично работает, но иногда, если я внесу изменения в файлы во время его работы, я получу эту странную ошибку в выводе отладчика, к которой я не могу найти источник.

2019-06-13 22:47:39,914 [ 257001]   WARN - com.intellij.util.xmlb.Binding - no accessors for class org.jetbrains.kotlin.idea.highlighter.KotlinDefaultHighlightingSettingsProvider
2019-06-13 22:47:51,772 [ 268859]   WARN - ntellij.ui.tree.AsyncTreeModel - ignore duplicated child at 1: module
2019-06-13 22:47:52,195 [ 269282]   WARN - ntellij.ui.tree.AsyncTreeModel - ignore duplicated child at 1: module
2019-06-13 22:48:37,693 [ 314780]   WARN - ntellij.ui.tree.AsyncTreeModel - ignore duplicated child at 1: module
2019-06-13 22:48:37,908 [ 314995]   WARN - ntellij.ui.tree.AsyncTreeModel - ignore duplicated child at 1: module
2019-06-13 22:48:38,183 [ 315270]   WARN - ntellij.ui.tree.AsyncTreeModel - ignore duplicated child at 1: module
2019-06-13 22:49:48,007 [ 385094]   WARN - ntellij.ui.tree.AsyncTreeModel - ignore duplicated child at 1: module
2019-06-13 22:49:48,214 [ 385301]   WARN - ntellij.ui.tree.AsyncTreeModel - ignore duplicated child at 1: module
2019-06-13 22:49:49,111 [ 386198]   WARN - ntellij.ui.tree.AsyncTreeModel - ignore duplicated child at 1: module
2019-06-13 22:49:49,348 [ 386435]   WARN - ntellij.ui.tree.AsyncTreeModel - ignore duplicated child at 1: module

Как говорится в сообщении об ошибке, похоже, что происходит сбой, когда он встречает два файла с одинаковыми именами на каком-то этапе процесса рендеринга или сортировки. Кажется, я не могу найти подходящее место, чтобы на самом деле поймать и решить эту проблему.

Я пишу плагин в Kotlin. Это первый раз, когда я использовал Kotlin, так что я немного новичок в этом - и я плохо разбираюсь в Java, так что это мало поможет.

В моем файле plugin.xml я настраиваю treeStructureProvider следующим образом;

<extensions defaultExtensionNs="com.intellij">
    <treeStructureProvider implementation="FoldingTreeStructureProvider" />
</extensions>

Я разместил класс FoldingTreeStructureProvider ниже, но держу его в нижней части вопроса для удобства чтения.

Ошибка обычно возникает, когда я переименовываю уже вложенный файл, поэтому я предполагаю, что запущен другой тип провайдера дерева, и он сталкивается с проблемой там. Я просто не уверен, где именно обращаться с этой логикой или что именно искать.

Это базовый FoldingTreeStructureProvider класс.

class FoldingTreeStructureProvider :
    com.intellij.ide.projectView.TreeStructureProvider {
    private lateinit var PATTERN_PREFIX: Pattern
    private lateinit var PATTERN_EXTENSION: Pattern
    /**
     * enable or disable grouping completely.
     */
    private val MatchPrefix: Boolean
        get() = PropertiesComponent.getInstance().getBoolean(
            FoldingConfiguration.ENABLE_PREFIX_MATCHING, true
        )
    /**
     * Mask the matched prefix when drawing nodes.
     */
    private val MaskPrefix: Boolean
        get() = PropertiesComponent.getInstance().getBoolean(
            FoldingConfiguration.MASK_PREFIX, true
        )

    private val MatchExtensions: Boolean
        get() = PropertiesComponent.getInstance().getBoolean(
            FoldingConfiguration.ENABLE_EXTENSIONS_MATCHING, true
        )

    override fun modify(
        parent: AbstractTreeNode<*>,
        children: Collection<AbstractTreeNode<*>>,
        viewSettings: ViewSettings
    ): Collection<AbstractTreeNode<*>> {
        /*
          by default, the files inside of a directory are the
          children for any node.
         */
        if (parent !is PsiDirectoryNode || !MatchPrefix)
            return children

        val project = parent.project
        val groups = HashMap<String, FoldableFileNodes>()
        val nodes = ArrayList<AbstractTreeNode<*>>()

        if (project == null)
            return emptyList()

        PATTERN_PREFIX = Pattern.compile(
            PropertiesComponent.getInstance().getValue(
                FoldingConfiguration.PATTERN_PREFIX,
                FoldingConfiguration.DEFAULT_PREFIX_PATTERN
            ),
            Pattern.CASE_INSENSITIVE
        )

        PATTERN_EXTENSION = Pattern.compile(
            PropertiesComponent.getInstance().getValue(
                FoldingConfiguration.PATTERN_EXTENSIONS,
                FoldingConfiguration.DEFAULT_EXTENSIONS_PATTERN
            ),
            Pattern.CASE_INSENSITIVE
        )
        /**
         * iterate each file and see if it is even a file
         */
        for (child in children) {
            if (child.value !is PsiFile) {
                nodes.add(child)
                continue
            }

            // get the filename
            // get the filename of the given node
            val filename = nameFromNode(child)
            val extensionMatcher = PATTERN_EXTENSION.matcher(filename)

            /**
             * if we have selected to limit matching only to certain extensions,
             * then we will check for that here.
             */
            if (!extensionMatcher.find() && MatchExtensions) {
                nodes.add(child)
                continue
            }


            // parse the filename with the regex PATTERN_PREFIX
            val groupMatcher = PATTERN_PREFIX.matcher(filename)

            // if no match was found, then we just add this
            // file to the tree as normal; Since we're going on
            // and building the rest of the tree uninterrupted.
            if (!groupMatcher.find()) {
                nodes.add(child)
                continue
            }

            // if there were matches found, we can proceed to
            // try and determine what group - if any, the file
            // should belong to.
            val prefix = groupMatcher.group(1)
            val type = groupMatcher.group(2)
            val extension = groupMatcher.group(3)
            // obtain the group key for this node
            val groupKey = String.format("%s", prefix)
            /**
             * obtain the group of nodes related to this collection
             */
            var group = groups[groupKey]
            /**
             * if a group is not found, then it needs to be created
             */
            if (group == null) {
                /**
                 * construct a new folding node section
                 */
                group = FoldableFileNodes(
                    project,
                    viewSettings,
                    prefix,
                    if (type.isNotBlank()) type else ""
                )

                /**
                 * ensure that the new group is assigned to the
                 * appropriate key for the next iteration
                 */
                groups[groupKey] = group
                nodes.add(group)
            }

            group.add(
                project,
                child,
                MaskPrefix,
                type,
                extension,
                prefix
            )
        }

        // undo grouping that only has children and no parent
        // elements.
        var i = 0
        while (i < nodes.size) {
            val added = nodes[i]
            if (added is FoldableFileNodes) {
                if (added.children.size <= 1) {
                    nodes[i] = added.root!!
                }
            }
            i++
        }

        return nodes
    }

    /**
     * retrieve the filename of any given node.
     */
    private fun nameFromNode(node: AbstractTreeNode<*>): String? {
        return (node.value as? PsiFile)?.name
    }
}
...