aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/org/polyfrost/sorbet
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/org/polyfrost/sorbet')
-rw-r--r--src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/Preprocessor.kt16
-rw-r--r--src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorCompletion.kt35
-rw-r--r--src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorExtend.kt56
-rw-r--r--src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorFolding.kt63
-rw-r--r--src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorHighlight.kt325
-rw-r--r--src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorImport.kt59
6 files changed, 554 insertions, 0 deletions
diff --git a/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/Preprocessor.kt b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/Preprocessor.kt
new file mode 100644
index 0000000..03cb762
--- /dev/null
+++ b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/Preprocessor.kt
@@ -0,0 +1,16 @@
+package org.polyfrost.sorbet.intelliprocessor
+
+val ALLOWED_TYPES = listOf("JAVA", "KOTLIN")
+
+enum class PreprocessorState {
+ NONE,
+ IF,
+ ELSE,
+}
+
+enum class PreprocessorDirective {
+ IF,
+ IFDEF,
+ ELSE,
+ ENDIF,
+}
diff --git a/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorCompletion.kt b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorCompletion.kt
new file mode 100644
index 0000000..b5f8417
--- /dev/null
+++ b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorCompletion.kt
@@ -0,0 +1,35 @@
+package org.polyfrost.sorbet.intelliprocessor
+
+import com.intellij.codeInsight.completion.*
+import com.intellij.codeInsight.lookup.LookupElementBuilder
+import com.intellij.openapi.project.DumbAware
+import com.intellij.patterns.PlatformPatterns.psiComment
+import com.intellij.patterns.StandardPatterns
+import com.intellij.util.ProcessingContext
+
+class PreprocessorCompletion : CompletionContributor(), DumbAware {
+ init {
+ extend(
+ CompletionType.BASIC,
+ psiComment().withText(
+ StandardPatterns.or(
+ StandardPatterns.string().startsWith("//"),
+ StandardPatterns.string().startsWith("#"),
+ ),
+ ),
+ PreprocessorCompletionProvider,
+ )
+ }
+
+ object PreprocessorCompletionProvider : CompletionProvider<CompletionParameters>() {
+ override fun addCompletions(
+ parameters: CompletionParameters,
+ context: ProcessingContext,
+ result: CompletionResultSet,
+ ) {
+ for (keyword in KEYWORDS) result.addElement(LookupElementBuilder.create(keyword).bold())
+ }
+
+ private val KEYWORDS = listOf("#if", "#else", "#elseif", "#endif", "#ifdef")
+ }
+}
diff --git a/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorExtend.kt b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorExtend.kt
new file mode 100644
index 0000000..db59737
--- /dev/null
+++ b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorExtend.kt
@@ -0,0 +1,56 @@
+package org.polyfrost.sorbet.intelliprocessor
+
+import com.intellij.codeInsight.editorActions.EnterHandler
+import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate.Result
+import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter
+import com.intellij.openapi.actionSystem.DataContext
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.actionSystem.EditorActionHandler
+import com.intellij.openapi.project.DumbAware
+import com.intellij.openapi.util.Ref
+import com.intellij.psi.PsiFile
+import com.intellij.psi.impl.source.tree.PsiCommentImpl
+import com.intellij.refactoring.suggested.startOffset
+import java.util.Locale
+
+class PreprocessorExtend : EnterHandlerDelegateAdapter(), DumbAware {
+ override fun preprocessEnter(
+ file: PsiFile,
+ editor: Editor,
+ caretOffset: Ref<Int>,
+ caretAdvance: Ref<Int>,
+ dataContext: DataContext,
+ originalHandler: EditorActionHandler?,
+ ): Result {
+ if (
+ EnterHandler.getLanguage(dataContext)
+ ?.associatedFileType
+ ?.name?.uppercase(Locale.getDefault()) !in ALLOWED_TYPES
+ ) {
+ return Result.Continue
+ }
+
+ val caret: Int = caretOffset.get().toInt()
+ val psiAtOffset = file.findElementAt(caret)
+
+ if (psiAtOffset is PsiCommentImpl) {
+ if (!psiAtOffset.text.startsWith("//$$")) return Result.Continue
+ val posInText = caret - psiAtOffset.startOffset
+ if (posInText < 4) return Result.DefaultForceIndent
+
+ editor.document.insertString(editor.caretModel.offset, "//$$ ")
+ caretAdvance.set(5)
+ return Result.DefaultForceIndent
+ } else if (psiAtOffset?.prevSibling is PsiCommentImpl) {
+ if (!psiAtOffset.prevSibling.text.startsWith("//$$")) return Result.Continue
+ val posInText = caret - psiAtOffset.prevSibling.startOffset
+ if (posInText < 4) return Result.DefaultForceIndent
+
+ editor.document.insertString(editor.caretModel.offset, "//$$ ")
+ caretAdvance.set(5)
+ return Result.DefaultForceIndent
+ }
+
+ return Result.Continue
+ }
+}
diff --git a/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorFolding.kt b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorFolding.kt
new file mode 100644
index 0000000..25d5ba0
--- /dev/null
+++ b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorFolding.kt
@@ -0,0 +1,63 @@
+package org.polyfrost.sorbet.intelliprocessor
+
+import com.intellij.lang.ASTNode
+import com.intellij.lang.LanguageCommenters
+import com.intellij.lang.folding.FoldingBuilderEx
+import com.intellij.lang.folding.FoldingDescriptor
+import com.intellij.openapi.editor.Document
+import com.intellij.openapi.project.DumbAware
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiComment
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiWhiteSpace
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.refactoring.suggested.endOffset
+import com.intellij.refactoring.suggested.startOffset
+
+class PreprocessorFolding : FoldingBuilderEx(), DumbAware {
+ override fun getPlaceholderText(node: ASTNode): String {
+ if (node !is PsiComment) return "...11".also { println("Not a comment? Is $node") }
+ val directivePrefix = (
+ LanguageCommenters.INSTANCE.forLanguage(node.language).lineCommentPrefix
+ ?: return "...222".also { println("Null comment prefix?") }
+ )
+ return (node as ASTNode).text.substring(directivePrefix.length)
+ }
+
+ override fun buildFoldRegions(
+ root: PsiElement,
+ document: Document,
+ quick: Boolean,
+ ): Array<FoldingDescriptor> {
+ val descriptors = mutableListOf<FoldingDescriptor>()
+ val directivePrefix =
+ (
+ LanguageCommenters.INSTANCE.forLanguage(root.language).lineCommentPrefix
+ ?: return emptyArray()
+ ) + "#"
+ val allDirectives =
+ PsiTreeUtil.findChildrenOfType(root, PsiComment::class.java)
+ .filter { it.text.startsWith(directivePrefix) }
+
+ for ((index, directive) in allDirectives.withIndex()) if (directive.text.run {
+ startsWith(directivePrefix + "if") ||
+ startsWith(directivePrefix + "ifdef") ||
+ startsWith(directivePrefix + "else")
+ } && index + 1 < allDirectives.size
+ ) {
+ val nextDirective = allDirectives[index + 1]
+ val endOffset =
+ when {
+ nextDirective.text.startsWith(directivePrefix + "endif") -> nextDirective.endOffset
+ nextDirective.prevSibling is PsiWhiteSpace -> nextDirective.prevSibling.startOffset
+ else -> nextDirective.startOffset
+ }
+
+ descriptors.add(FoldingDescriptor(directive, TextRange(directive.startOffset, endOffset)))
+ }
+
+ return descriptors.toTypedArray()
+ }
+
+ override fun isCollapsedByDefault(node: ASTNode) = false
+}
diff --git a/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorHighlight.kt b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorHighlight.kt
new file mode 100644
index 0000000..9eba31b
--- /dev/null
+++ b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorHighlight.kt
@@ -0,0 +1,325 @@
+package org.polyfrost.sorbet.intelliprocessor
+
+import com.intellij.codeInsight.daemon.impl.HighlightInfo
+import com.intellij.codeInsight.daemon.impl.HighlightInfoType
+import com.intellij.codeInsight.daemon.impl.HighlightVisitor
+import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
+import com.intellij.lang.Commenter
+import com.intellij.lang.LanguageCommenters
+import com.intellij.lang.annotation.HighlightSeverity
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
+import com.intellij.openapi.editor.colors.EditorColorsManager
+import com.intellij.openapi.editor.colors.TextAttributesKey
+import com.intellij.openapi.editor.markup.TextAttributes
+import com.intellij.openapi.fileTypes.SyntaxHighlighter
+import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory
+import com.intellij.openapi.project.DumbAware
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import com.intellij.psi.impl.source.tree.PsiCommentImpl
+import com.intellij.refactoring.suggested.endOffset
+import java.awt.Font
+import java.util.*
+import java.util.regex.Pattern
+
+class PreprocessorHighlight(private val project: Project) : HighlightVisitor, DumbAware {
+ private lateinit var holder: HighlightInfoHolder
+ private lateinit var commenter: Commenter
+ private lateinit var highlighter: SyntaxHighlighter
+
+ private var preprocessorState = ArrayDeque<PreprocessorState>()
+
+ override fun suitableForFile(file: PsiFile): Boolean {
+ return file.fileType.name.uppercase(Locale.getDefault()) in ALLOWED_TYPES
+ }
+
+ override fun clone(): PreprocessorHighlight {
+ return PreprocessorHighlight(project)
+ }
+
+ override fun analyze(
+ file: PsiFile,
+ updateWholeFile: Boolean,
+ holder: HighlightInfoHolder,
+ action: Runnable,
+ ): Boolean {
+ this.holder = holder
+ this.commenter = LanguageCommenters.INSTANCE.forLanguage(file.language)
+ this.highlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(file.language, file.project, file.virtualFile)
+
+ action.run()
+ return true
+ }
+
+ override fun visit(element: PsiElement) {
+ if (element !is PsiCommentImpl) return
+ val commentSource = element.text
+ if (commenter.lineCommentPrefix?.let {
+ commentSource.startsWith(it)
+ } != true
+ ) {
+ return
+ }
+
+ val prefixLength = commenter.lineCommentPrefix?.length ?: return
+
+ val comment = commentSource.substring(prefixLength)
+ if (comment.isEmpty()) return
+
+ EditorColorsManager.getInstance()
+
+ if (comment.startsWith("#")) {
+ val commentSegments = comment.substring(1).split(WHITESPACES_PATTERN, limit = 2)
+
+ when (val directive = commentSegments[0]) {
+ "if", "elseif" -> {
+ if (directive == "elseif") {
+ val existingIf = preprocessorState.pollFirst()
+ if (existingIf != PreprocessorState.IF) {
+ fail(
+ element,
+ "Preprocessor directive \"elseif\" must have a preceding \"if\" or \"elseif\".",
+ )
+ return
+ }
+ }
+
+ preprocessorState.push(PreprocessorState.IF)
+
+ holder.add(directive.toDirectiveHighlight(element, prefixLength))
+
+ if (commentSegments.size < 2) {
+ fail(element, "Preprocessor directive \"$directive\" is missing a condition.", eol = true)
+ return
+ }
+
+ val conditionsSource = commentSegments[1]
+ val conditions = conditionsSource.split(SPLIT_PATTERN)
+
+ var nextStartPos = prefixLength + 3
+ for (condition in conditions) {
+ val trimmedCondition = condition.trim()
+
+ val position = commentSource.indexOf(trimmedCondition, nextStartPos)
+ nextStartPos = position + trimmedCondition.length
+
+ val conditionMatcher = EXPR_PATTERN.find(trimmedCondition)
+
+ if (conditionMatcher == null || conditionMatcher.groups.size < 4) {
+ val identifierMatcher = IDENTIFIER_PATTERN.matchEntire(trimmedCondition)
+
+ if (identifierMatcher != null) {
+ holder.add(identifierMatcher.groups[0]?.toNumericOrVariableHighlight(element, position))
+ } else {
+ holder.add(trimmedCondition.toInvalidConditionErrorHighlight(element, position))
+ }
+
+ continue
+ }
+
+ holder.add(conditionMatcher.groups[1]?.toNumericOrVariableHighlight(element, position))
+ holder.add(conditionMatcher.groups[3]?.toNumericOrVariableHighlight(element, position))
+ }
+ }
+
+ "ifdef" -> {
+ preprocessorState.push(PreprocessorState.IF)
+
+ holder.add(directive.toDirectiveHighlight(element, prefixLength))
+
+ if (commentSegments.size < 2) {
+ fail(element, "Preprocessor directive \"ifdef\" is missing an identifier.", eol = true)
+ return
+ }
+
+ val idInfo =
+ HighlightInfo
+ .newHighlightInfo(IDENTIFIER_TYPE)
+ .range(
+ element as PsiElement,
+ element.startOffset + prefixLength + 7,
+ element.startOffset + prefixLength + 7 + commentSegments[1].length,
+ )
+ .textAttributes(IDENTIFIER_ATTRIBUTES)
+ .create()
+
+ holder.add(idInfo)
+ }
+
+ "else" -> {
+ val state = preprocessorState.pollFirst()
+ preprocessorState.push(PreprocessorState.ELSE)
+
+ if (state != PreprocessorState.IF) {
+ fail(element, "Preprocessor directive \"else\" must have an opening if.")
+ return
+ }
+
+ if (commentSegments.size > 1) {
+ fail(element, "Preprocessor directive \"else\" does not require any arguments.")
+ return
+ }
+
+ holder.add(directive.toDirectiveHighlight(element, prefixLength))
+ }
+
+ "endif" -> {
+ val state = preprocessorState.pollFirst()
+
+ if (state != PreprocessorState.IF && state != PreprocessorState.ELSE) {
+ fail(element, "Preprocessor directive \"endif\" must have an opening if.")
+ return
+ }
+
+ if (commentSegments.size > 1) {
+ fail(element, "Preprocessor directive \"endif\" does not require any arguments.")
+ return
+ }
+
+ holder.add(directive.toDirectiveHighlight(element, prefixLength))
+ }
+
+ else -> {
+ fail(element, "Unknown preprocessor directive \"$directive\"")
+ }
+ }
+ } else if (comment.startsWith("$$")) {
+ holder.add("$$".toDirectiveHighlight(element, prefixLength))
+
+ highlightCodeBlock(element, element.startOffset + prefixLength + 2, comment.substring(2))
+ }
+ }
+
+ private fun highlightCodeBlock(
+ element: PsiCommentImpl,
+ startOffset: Int,
+ text: String,
+ ) {
+ val lexer = highlighter.highlightingLexer
+
+ lexer.start(text)
+ var token = lexer.tokenType
+
+ while (token != null) {
+ val attributes =
+ highlighter.getTokenHighlights(token)
+ .fold(TextAttributes(null, null, null, null, 0)) { first, second ->
+ TextAttributes.merge(first, SCHEME.getAttributes(second))
+ }
+
+ val directiveInfo =
+ HighlightInfo
+ .newHighlightInfo(HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT)
+ .range(
+ element as PsiElement,
+ startOffset + lexer.tokenStart,
+ startOffset + lexer.tokenEnd,
+ )
+ .textAttributes(attributes)
+ .create()
+
+ holder.add(directiveInfo)
+
+ lexer.advance()
+ token = lexer.tokenType
+ }
+ }
+
+ private fun fail(
+ element: PsiElement,
+ text: String,
+ eol: Boolean = false,
+ ) {
+ val info =
+ HighlightInfo
+ .newHighlightInfo(HighlightInfoType.ERROR)
+ .descriptionAndTooltip(text)
+ .apply {
+ if (eol) {
+ endOfLine()
+ range(element.endOffset, element.endOffset)
+ } else {
+ range(element)
+ }
+ }
+ .create()
+
+ holder.add(info)
+ }
+
+ private fun MatchGroup.toNumericOrVariableHighlight(
+ element: PsiCommentImpl,
+ offset: Int = 0,
+ ): HighlightInfo? {
+ val builder =
+ if (value.trim().toIntOrNull() != null) {
+ HighlightInfo
+ .newHighlightInfo(NUMBER_TYPE)
+ .textAttributes(NUMBER_ATTRIBUTES)
+ } else {
+ HighlightInfo
+ .newHighlightInfo(IDENTIFIER_TYPE)
+ .textAttributes(IDENTIFIER_ATTRIBUTES)
+ }
+
+ return builder
+ .range(element, element.startOffset + offset + range.first, element.startOffset + offset + range.last + 1)
+ .create()
+ }
+
+ private fun String.toDirectiveHighlight(
+ element: PsiCommentImpl,
+ offset: Int = 0,
+ ): HighlightInfo? {
+ return HighlightInfo
+ .newHighlightInfo(DIRECTIVE_TYPE)
+ .textAttributes(DIRECTIVE_ATTRIBUTES)
+ .range(element, element.startOffset + offset, element.startOffset + offset + 1 + length)
+ .create()
+ }
+
+ private fun String.toInvalidConditionErrorHighlight(
+ element: PsiCommentImpl,
+ offset: Int = 0,
+ ): HighlightInfo? {
+ return HighlightInfo
+ .newHighlightInfo(HighlightInfoType.ERROR)
+ .range(element, element.startOffset + offset, element.startOffset + offset + length)
+ .descriptionAndTooltip("Invalid condition \"$this\"")
+ .create()
+ }
+
+ companion object {
+ private val BOLD_ATTRIBUTE = TextAttributes(null, null, null, null, Font.BOLD)
+ val SCHEME = EditorColorsManager.getInstance().globalScheme
+
+ private val DIRECTIVE_COLOR: TextAttributesKey = DefaultLanguageHighlighterColors.KEYWORD
+ val DIRECTIVE_ATTRIBUTES: TextAttributes =
+ TextAttributes.merge(SCHEME.getAttributes(DIRECTIVE_COLOR), BOLD_ATTRIBUTE)
+ val DIRECTIVE_TYPE = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, DIRECTIVE_COLOR)
+
+ private val OPERATOR_COLOR: TextAttributesKey = DefaultLanguageHighlighterColors.OPERATION_SIGN
+ val OPERATOR_ATTRIBUTES = SCHEME.getAttributes(OPERATOR_COLOR)
+ val OPERATOR_TYPE = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, OPERATOR_COLOR)
+
+ private val IDENTIFIER_COLOR: TextAttributesKey = DefaultLanguageHighlighterColors.IDENTIFIER
+ val IDENTIFIER_ATTRIBUTES: TextAttributes =
+ TextAttributes.merge(SCHEME.getAttributes(IDENTIFIER_COLOR), BOLD_ATTRIBUTE)
+ val IDENTIFIER_TYPE = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, IDENTIFIER_COLOR)
+
+ private val NUMBER_COLOR: TextAttributesKey = DefaultLanguageHighlighterColors.NUMBER
+ val NUMBER_ATTRIBUTES: TextAttributes = TextAttributes.merge(SCHEME.getAttributes(NUMBER_COLOR), BOLD_ATTRIBUTE)
+ val NUMBER_TYPE = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, NUMBER_COLOR)
+
+ private val LOGGER: Logger = Logger.getInstance(PreprocessorHighlight::class.java)
+
+ private val WHITESPACES_PATTERN = "\\s+".toRegex()
+ private val EXPR_PATTERN = "(.+)(==|!=|<=|>=|<|>)(.+)".toRegex()
+ private val IDENTIFIER_PATTERN = "[A-Za-z0-9]+".toRegex()
+ private val OR_PATTERN = Pattern.quote("||")
+ private val AND_PATTERN = Pattern.quote("&&")
+ private val SPLIT_PATTERN = Pattern.compile("$OR_PATTERN|$AND_PATTERN")
+ }
+}
diff --git a/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorImport.kt b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorImport.kt
new file mode 100644
index 0000000..1072ee0
--- /dev/null
+++ b/src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorImport.kt
@@ -0,0 +1,59 @@
+package org.polyfrost.sorbet.intelliprocessor
+
+import com.intellij.lang.ImportOptimizer
+import com.intellij.lang.LanguageImportStatements
+import com.intellij.lang.java.JavaImportOptimizer
+import com.intellij.openapi.util.EmptyRunnable
+import com.intellij.psi.*
+import com.intellij.psi.codeStyle.JavaCodeStyleManager
+import com.intellij.psi.impl.source.tree.PsiCommentImpl
+
+class PreprocessorImport : ImportOptimizer {
+ override fun supports(file: PsiFile): Boolean {
+ return file is PsiJavaFile
+ }
+
+ override fun processFile(file: PsiFile): Runnable {
+ if (file !is PsiJavaFile) return EmptyRunnable.getInstance()
+ val imports = file.importList ?: return EmptyRunnable.getInstance()
+
+ if (!hasPreprocessorDirectives(imports)) {
+ return LanguageImportStatements.INSTANCE
+ .allForLanguage(file.language)
+ .first { it !is JavaImportOptimizer }
+ .processFile(file)
+ }
+
+ val optimizedImportList =
+ JavaCodeStyleManager
+ .getInstance(file.project)
+ .prepareOptimizeImportsResult(file)
+
+ return Runnable {
+ val manager = PsiDocumentManager.getInstance(file.project)
+ val document = manager.getDocument(file)
+ if (document != null) manager.commitDocument(document)
+
+ for (import in imports.importStatements)
+ if (optimizedImportList.findSingleClassImportStatement(import.qualifiedName) == null) {
+ import.delete()
+ }
+
+ if (imports.firstChild is PsiWhiteSpace) imports.firstChild.delete()
+ }
+ }
+
+ private fun hasPreprocessorDirectives(imports: PsiImportList): Boolean {
+ var import = imports.firstChild
+
+ while (import != null) {
+ if (import is PsiCommentImpl && import.text.startsWith("//#")) {
+ return true
+ } else {
+ import = import.nextSibling
+ }
+ }
+
+ return false
+ }
+}