From d95fe9ab23f4ce19ed1a17b13f995aceefd14fd8 Mon Sep 17 00:00:00 2001 From: Pauline Date: Sat, 27 Apr 2024 19:59:05 -0400 Subject: ♻️ refactor(plugin): refactor cc -> org and update deps, intellij MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 42 ++- .gitattributes | 30 ++ .gitignore | 46 +-- .gitmodules | 3 + build.gradle.kts | 10 +- detekt.yml | 53 ++++ format | 1 + gradle.properties | 8 +- gradle/libs.versions.toml | 6 +- gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 3 +- settings.gradle.kts | 4 +- .../sorbet/intelliprocessor/Preprocessor.kt | 11 - .../intelliprocessor/PreprocessorCompletion.kt | 35 --- .../sorbet/intelliprocessor/PreprocessorExtend.kt | 54 ---- .../sorbet/intelliprocessor/PreprocessorFolding.kt | 51 ---- .../intelliprocessor/PreprocessorHighlight.kt | 300 ------------------- .../sorbet/intelliprocessor/PreprocessorImport.kt | 54 ---- .../sorbet/intelliprocessor/Preprocessor.kt | 16 + .../intelliprocessor/PreprocessorCompletion.kt | 35 +++ .../sorbet/intelliprocessor/PreprocessorExtend.kt | 56 ++++ .../sorbet/intelliprocessor/PreprocessorFolding.kt | 63 ++++ .../intelliprocessor/PreprocessorHighlight.kt | 325 +++++++++++++++++++++ .../sorbet/intelliprocessor/PreprocessorImport.kt | 59 ++++ src/main/resources/META-INF/plugin.xml | 18 +- 26 files changed, 716 insertions(+), 569 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitmodules create mode 100644 detekt.yml create mode 160000 format delete mode 100644 src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/Preprocessor.kt delete mode 100644 src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorCompletion.kt delete mode 100644 src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorExtend.kt delete mode 100644 src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorFolding.kt delete mode 100644 src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorHighlight.kt delete mode 100644 src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorImport.kt create mode 100644 src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/Preprocessor.kt create mode 100644 src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorCompletion.kt create mode 100644 src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorExtend.kt create mode 100644 src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorFolding.kt create mode 100644 src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorHighlight.kt create mode 100644 src/main/kotlin/org/polyfrost/sorbet/intelliprocessor/PreprocessorImport.kt diff --git a/.editorconfig b/.editorconfig index db6b96d..0901bba 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,40 @@ -[*.{gradle,java}] -ij_java_class_count_to_use_import_on_demand = 999 \ No newline at end of file +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = tab +tab_width = 4 +trim_trailing_whitespace = true + +[*.gradle] +indent_style = tab + +[*.java] +indent_style = tab +ij_continuation_indent_size = 8 +ij_java_imports_layout = $*,|,java.**,|,javax.**,|,*,|,net.minecraft.**,|,org.polyfrost.** +ij_java_class_count_to_use_import_on_demand = 999 + +[*.json] +indent_style = space +indent_size = 2 + +[.editorconfig] +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[*.gradle] +indent_style = tab + +[*.toml] +indent_style = tab +tab_width = 2 + +[*.properties] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..99d0367 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,30 @@ +*.java text eol=lf diff=java +*.gradle text eol=lf diff=java +*.kt text eol=lf diff=kotlin +*.kts text eol=lf diff=kotlin +gradlew text eol=lf +*.bat text eol=crlf + +*.md text eol=lf diff=markdown + +.editorconfig text eol=lf + +*.json text eol=lf +*.json5 text eol=lf +*.properties text eol=lf +*.toml text eol=lf +*.xml text eol=lf diff=html + +# Modding specific +*.accesswidener text eol=lf + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.class binary +*.dll binary +*.ear binary +*.jar binary +*.jks binary +*.png binary +*.so binary +*.war binary diff --git a/.gitignore b/.gitignore index f8d108f..aabe5fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,3 @@ -# eclipse -bin -*.launch -.settings -.metadata -.classpath -.project - -# idea -out -*.ipr -*.iws -*.iml -.idea - -# fleet -.fleet - -# gradle -build -.gradle - -# other -eclipse -run -build - Copy.gradle -git-story_media -.vscode -.devauth -.DS_STORE - - # Created by https://www.toptal.com/developers/gitignore/api/java,gradle,forgegradle,kotlin,macos,intellij,intellij+all,eclipse,visualstudiocode # Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,forgegradle,kotlin,macos,intellij,intellij+all,eclipse,visualstudiocode @@ -365,12 +333,6 @@ Temporary Items .history .ionide -# Support for Project snippet scope -.vscode/*.code-snippets - -# Ignore code-workspaces -*.code-workspace - ### Gradle ### .gradle **/build/ @@ -385,8 +347,6 @@ gradle-app.setting # Avoid ignore Gradle wrappper properties !gradle-wrapper.properties -!*.api - # Cache of project .gradletasknamecache @@ -396,4 +356,8 @@ gradle-app.setting # JDT-specific (Eclipse Java Development Tools) .classpath -# End of https://www.toptal.com/developers/gitignore/api/java,gradle,forgegradle,kotlin,macos,intellij,intellij+all,eclipse,visualstudiocode \ No newline at end of file +### Gradle Patch ### +# Java heap dump +*.hprof + +# End of https://www.toptal.com/developers/gitignore/api/java,gradle,forgegradle,kotlin,macos,intellij,intellij+all,eclipse,visualstudiocode diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ea4d92e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "format"] + path = format + url = https://github.com/polyfrost/buildformat diff --git a/build.gradle.kts b/build.gradle.kts index 8668e3e..da0ea43 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,17 +4,23 @@ plugins { id("java") alias(libs.plugins.kotlin) alias(libs.plugins.intellij) + alias(libs.plugins.kotlinter) } group = properties("pluginGroup").get() version = properties("pluginVersion").get() repositories { - maven("https://repo.polyfrost.cc/releases") + maven("https://repo.polyfrost.org/releases") } kotlin { - jvmToolchain(17) + jvmToolchain(21) +} + +kotlinter { + ignoreFailures = false + reporters = arrayOf("checkstyle", "plain") } intellij { diff --git a/detekt.yml b/detekt.yml new file mode 100644 index 0000000..e944b47 --- /dev/null +++ b/detekt.yml @@ -0,0 +1,53 @@ +build: + maxIssues: 0 + weights: + comments: 0 + +style: + CanBeNonNullable: + active: true + CollapsibleIfStatements: + active: true + LibraryCodeMustSpecifyReturnType: + active: true + MagicNumber: + active: true + ignorePropertyDeclaration: true + ignoreAnnotation: true + ignoreEnums: true + MaxLineLength: + active: true + maxLineLength: 120 + MandatoryBracesIfStatements: + active: true + MandatoryBracesLoops: + active: true + NewLineAtEndOfFile: + active: true + UnusedImports: + active: true + UnusedPrivateMember: + active: true + UnusedPrivateClass: + active: true + +naming: + ClassNaming: + active: true + FunctionNaming: + active: true + +coroutines: + active: true + RedundantSuspendModifier: + active: true + +comments: + UndocumentedPublicClass: + active: true + UndocumentedPublicFunction: + active: true + UndocumentedPublicProperty: + active: true + EndOfSentenceFormat: + active: true diff --git a/format b/format new file mode 160000 index 0000000..4cf6d56 --- /dev/null +++ b/format @@ -0,0 +1 @@ +Subproject commit 4cf6d56acce63bf7f553638c86e835c203a420c0 diff --git a/gradle.properties b/gradle.properties index a9b6fee..4653e92 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ -pluginGroup=cc.polyfrost.sorbet.intelliprocessor +pluginGroup=org.polyfrost.sorbet.intelliprocessor pluginName=IntelliProcessor pluginRepositoryUrl=https://github.com/Polyfrost/IntelliProcessor -pluginVersion=1.0.0 +pluginVersion=1.0.1 pluginSinceBuild=223 -pluginUntilBuild=232.* +pluginUntilBuild=242.* platformType=IC platformVersion=2022.3.3 platformPlugins=com.intellij.java @@ -14,4 +14,4 @@ org.gradle.daemon=true org.gradle.parallel=true org.gradle.configureoncommand=true org.gradle.parallel.threads=4 -org.gradle.jvmargs=-Xmx4G \ No newline at end of file +org.gradle.jvmargs=-Xmx4G diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 30e4cc4..3979485 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,9 @@ [versions] -kotlin = "1.9.0" -intellij = "1.15.0" +kotlin = "1.9.23" +intellij = "1.17.3" +kotlinter = "4.3.0" [plugins] kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } intellij = { id = "org.jetbrains.intellij", version.ref = "intellij" } +kotlinter = { id = "org.jmailen.kotlinter", version.ref = "kotlinter" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c..7f93135 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ac72c34..b82aa23 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index fcb6fca..0adc8e1 100755 --- a/gradlew +++ b/gradlew @@ -83,7 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/settings.gradle.kts b/settings.gradle.kts index 2faaf35..daf6e92 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,8 +1,8 @@ pluginManagement { repositories { gradlePluginPortal() - maven("https://repo.polyfrost.cc/releases") + maven("https://repo.polyfrost.org/releases") } } -rootProject.name = "IntelliProcessor" \ No newline at end of file +rootProject.name = "IntelliProcessor" diff --git a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/Preprocessor.kt b/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/Preprocessor.kt deleted file mode 100644 index a79cb4e..0000000 --- a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/Preprocessor.kt +++ /dev/null @@ -1,11 +0,0 @@ -package cc.polyfrost.sorbet.intelliprocessor - -val ALLOWED_TYPES = listOf("JAVA", "KOTLIN") - -enum class PreprocessorState { - NONE, IF, ELSE -} - -enum class PreprocessorDirective { - IF, IFDEF, ELSE, ENDIF -} \ No newline at end of file diff --git a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorCompletion.kt b/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorCompletion.kt deleted file mode 100644 index ce66bc5..0000000 --- a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorCompletion.kt +++ /dev/null @@ -1,35 +0,0 @@ -package cc.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() { - 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") - } -} \ No newline at end of file diff --git a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorExtend.kt b/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorExtend.kt deleted file mode 100644 index 2c7ea43..0000000 --- a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorExtend.kt +++ /dev/null @@ -1,54 +0,0 @@ -package cc.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, - caretAdvance: Ref, - 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 - } -} \ No newline at end of file diff --git a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorFolding.kt b/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorFolding.kt deleted file mode 100644 index 1bcbb66..0000000 --- a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorFolding.kt +++ /dev/null @@ -1,51 +0,0 @@ -package cc.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 { - val descriptors = mutableListOf() - 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 -} \ No newline at end of file diff --git a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorHighlight.kt b/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorHighlight.kt deleted file mode 100644 index 2442028..0000000 --- a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorHighlight.kt +++ /dev/null @@ -1,300 +0,0 @@ -package cc.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() - - 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") - } -} \ No newline at end of file diff --git a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorImport.kt b/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorImport.kt deleted file mode 100644 index 5ccf6f5..0000000 --- a/src/main/kotlin/cc/polyfrost/sorbet/intelliprocessor/PreprocessorImport.kt +++ /dev/null @@ -1,54 +0,0 @@ -package cc.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 - } -} \ No newline at end of file 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() { + 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, + caretAdvance: Ref, + 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 { + val descriptors = mutableListOf() + 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() + + 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 + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 9267a13..d20684e 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -1,10 +1,10 @@ - cc.polyfrost.sorbet.intelliprocessor + org.polyfrost.sorbet.intelliprocessor IntelliProcessor - Polyfrost + Polyfrost preprocessor syntax ]]> @@ -13,25 +13,25 @@ - \ No newline at end of file + -- cgit