Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class PreprocessorImport : ImportOptimizer {
if (!hasPreprocessorDirectives(imports)) {
return LanguageImportStatements.INSTANCE
.allForLanguage(file.language)
.first { it !is JavaImportOptimizer }
.first { it is JavaImportOptimizer }
.processFile(file)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ class PluginConfigurable : Configurable {
private lateinit var inspectionHighlightCommentsNotMatchingIfIndentsCheckbox: JCheckBox
private lateinit var hideUnmatchedVersionsCheckbox: JCheckBox
private lateinit var addPreprocessorCommentOnEnterCheckbox: JCheckBox

private lateinit var colorNestedPreprocessorCommentsCheckbox: JCheckBox
private lateinit var colorNestedPreprocessorCommentsOnlyOnSameIndentCheckbox: JCheckBox
private lateinit var inspectionHighlightContentNotMatchingIfIndentsCheckbox: JCheckBox
private lateinit var inspectionRequireJavaKotlinSpacingInConditionsCheckbox: JCheckBox

override fun getDisplayName(): String = "IntelliProcessor"

Expand Down Expand Up @@ -57,6 +60,19 @@ class PluginConfigurable : Configurable {
addPreprocessorCommentOnEnterCheckbox = JCheckBox("Add preprocessor comment '//$$ ' automatically to new lines in a disabled preprocessor block")
.tooltip("When pressing Enter inside a disabled preprocessor block, automatically adds a preprocessor comment '//$$ ' to the new line.")

colorNestedPreprocessorCommentsCheckbox = JCheckBox("Color nested preprocessor comments differently")
.tooltip("Colors preprocessor comments inside nested preprocessor blocks with a different color for better visibility.")

colorNestedPreprocessorCommentsOnlyOnSameIndentCheckbox = JCheckBox("Only recolor nested preprocessor comments when in-line with each other")
.tooltip("Only applies the different color to nested preprocessor comments when their indents align.")

inspectionHighlightContentNotMatchingIfIndentsCheckbox = JCheckBox("Highlight lines indented less than their \"if\"'s indent")
.tooltip("Highlights lines of code inside preprocessor blocks that are indented less than the corresponding \"if\" directive.\n" +
"This does break preprocessing.")

inspectionRequireJavaKotlinSpacingInConditionsCheckbox = JCheckBox("Highlight non-standard Java / Kotlin spacing around operators in preprocessor conditions")
.tooltip("Highlights improper use of spaces around operators in preprocessor conditions for improved readability.")

// Arrange components

fun titledBlock(str: String, block: JPanel.() -> Unit): JPanel = JPanel().apply {
Expand All @@ -78,6 +94,10 @@ class PluginConfigurable : Configurable {
add(titledBlock("Formatting") {
add(inspectionHighlightNonIndentedNestedIfsCheckbox)
add(inspectionHighlightCommentsNotMatchingIfIndentsCheckbox)
add(inspectionHighlightContentNotMatchingIfIndentsCheckbox)
add(colorNestedPreprocessorCommentsCheckbox)
add(colorNestedPreprocessorCommentsOnlyOnSameIndentCheckbox)
add(inspectionRequireJavaKotlinSpacingInConditionsCheckbox)
})

add(titledBlock("Jump To Pre-Processed File Action") {
Expand All @@ -104,6 +124,10 @@ class PluginConfigurable : Configurable {
|| inspectionHighlightCommentsNotMatchingIfIndentsCheckbox.isSelected != PluginSettings.instance.inspectionHighlightCommentsNotMatchingIfIndents
|| hideUnmatchedVersionsCheckbox.isSelected != PluginSettings.instance.hideUnmatchedVersions
|| addPreprocessorCommentOnEnterCheckbox.isSelected != PluginSettings.instance.addPreprocessorCommentOnEnter
|| colorNestedPreprocessorCommentsCheckbox.isSelected != PluginSettings.instance.colorNestedPreprocessorComments
|| colorNestedPreprocessorCommentsOnlyOnSameIndentCheckbox.isSelected != PluginSettings.instance.colorNestedPreprocessorCommentsOnlyOnSameIndent
|| inspectionHighlightContentNotMatchingIfIndentsCheckbox.isSelected != PluginSettings.instance.inspectionHighlightContentNotMatchingIfIndents
|| inspectionRequireJavaKotlinSpacingInConditionsCheckbox.isSelected != PluginSettings.instance.inspectionRequireJavaKotlinSpacingInConditions

override fun apply() {
PluginSettings.instance.foldAllBlocksByDefault = foldAllBlocksByDefaultCheckbox.isSelected
Expand All @@ -112,6 +136,10 @@ class PluginConfigurable : Configurable {
PluginSettings.instance.inspectionHighlightCommentsNotMatchingIfIndents = inspectionHighlightCommentsNotMatchingIfIndentsCheckbox.isSelected
PluginSettings.instance.hideUnmatchedVersions = hideUnmatchedVersionsCheckbox.isSelected
PluginSettings.instance.addPreprocessorCommentOnEnter = addPreprocessorCommentOnEnterCheckbox.isSelected
PluginSettings.instance.colorNestedPreprocessorComments = colorNestedPreprocessorCommentsCheckbox.isSelected
PluginSettings.instance.colorNestedPreprocessorCommentsOnlyOnSameIndent = colorNestedPreprocessorCommentsOnlyOnSameIndentCheckbox.isSelected
PluginSettings.instance.inspectionHighlightContentNotMatchingIfIndents = inspectionHighlightContentNotMatchingIfIndentsCheckbox.isSelected
PluginSettings.instance.inspectionRequireJavaKotlinSpacingInConditions = inspectionRequireJavaKotlinSpacingInConditionsCheckbox.isSelected
}

override fun reset() {
Expand All @@ -121,5 +149,9 @@ class PluginConfigurable : Configurable {
inspectionHighlightCommentsNotMatchingIfIndentsCheckbox.isSelected = PluginSettings.instance.inspectionHighlightCommentsNotMatchingIfIndents
hideUnmatchedVersionsCheckbox.isSelected = PluginSettings.instance.hideUnmatchedVersions
addPreprocessorCommentOnEnterCheckbox.isSelected = PluginSettings.instance.addPreprocessorCommentOnEnter
colorNestedPreprocessorCommentsCheckbox.isSelected = PluginSettings.instance.colorNestedPreprocessorComments
colorNestedPreprocessorCommentsOnlyOnSameIndentCheckbox.isSelected = PluginSettings.instance.colorNestedPreprocessorCommentsOnlyOnSameIndent
inspectionHighlightContentNotMatchingIfIndentsCheckbox.isSelected = PluginSettings.instance.inspectionHighlightContentNotMatchingIfIndents
inspectionRequireJavaKotlinSpacingInConditionsCheckbox.isSelected = PluginSettings.instance.inspectionRequireJavaKotlinSpacingInConditions
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import com.intellij.openapi.components.service
class PluginSettings : PersistentStateComponent<PluginSettings> {
var foldAllBlocksByDefault: Boolean = false
var foldInactiveBlocksByDefault: Boolean = true
var inspectionHighlightNonIndentedNestedIfs: Boolean = true
var inspectionHighlightNonIndentedNestedIfs: Boolean = false
var inspectionHighlightCommentsNotMatchingIfIndents: Boolean = true
var hideUnmatchedVersions: Boolean = false
var addPreprocessorCommentOnEnter = true
var colorNestedPreprocessorComments = true
var colorNestedPreprocessorCommentsOnlyOnSameIndent = false
var inspectionHighlightContentNotMatchingIfIndents = true
var inspectionRequireJavaKotlinSpacingInConditions = false

override fun getState(): PluginSettings = this

Expand All @@ -25,6 +29,10 @@ class PluginSettings : PersistentStateComponent<PluginSettings> {
this.inspectionHighlightCommentsNotMatchingIfIndents = state.inspectionHighlightCommentsNotMatchingIfIndents
this.hideUnmatchedVersions = state.hideUnmatchedVersions
this.addPreprocessorCommentOnEnter = state.addPreprocessorCommentOnEnter
this.colorNestedPreprocessorComments = state.colorNestedPreprocessorComments
this.colorNestedPreprocessorCommentsOnlyOnSameIndent = state.colorNestedPreprocessorCommentsOnlyOnSameIndent
this.inspectionHighlightContentNotMatchingIfIndents = state.inspectionHighlightContentNotMatchingIfIndents
this.inspectionRequireJavaKotlinSpacingInConditions = state.inspectionRequireJavaKotlinSpacingInConditions
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
import com.intellij.psi.impl.source.tree.PsiCommentImpl
import com.intellij.ui.JBColor
import org.polyfrost.intelliprocessor.ALLOWED_FILE_TYPES
import org.polyfrost.intelliprocessor.Scope
import org.polyfrost.intelliprocessor.config.PluginSettings
import java.util.ArrayDeque
import java.util.Locale
import org.polyfrost.intelliprocessor.utils.PreprocessorVersion.Companion.toPreprocessorIntOrNull
import java.awt.Color
import java.util.*

val SCHEME = EditorColorsManager.getInstance().globalScheme

Expand All @@ -35,16 +37,23 @@ fun TextAttributes.fade(): TextAttributes {
val r = c.red + (COMMENT_COLOR.red - c.red) / 2
val g = c.green + (COMMENT_COLOR.green - c.green) / 2
val b = c.blue + (COMMENT_COLOR.blue - c.blue) / 2
java.awt.Color(r, g, b)
Color(r, g, b)
}
return this
}

fun fadedItalic(color: Color? = null): TextAttributes {
val attributes = TextAttributes.merge(SCHEME.getAttributes(DIRECTIVE_COLOR), ITALIC_ATTRIBUTE)
if (color != null) attributes.foregroundColor = color
return attributes.fade()
}

val BOLD_ATTRIBUTE = TextAttributes(null, null, null, null, java.awt.Font.BOLD)
val ITALIC_ATTRIBUTE = TextAttributes(null, null, null, null, java.awt.Font.ITALIC)

val DIRECTIVE_COLOR: TextAttributesKey = DefaultLanguageHighlighterColors.KEYWORD
val DIRECTIVE_ATTRIBUTES: TextAttributes = TextAttributes.merge(SCHEME.getAttributes(DIRECTIVE_COLOR), ITALIC_ATTRIBUTE).fade()
val DIRECTIVE_ATTRIBUTES_NESTED = listOf(null, JBColor.YELLOW, JBColor.GREEN, JBColor.CYAN, JBColor.BLUE, JBColor.MAGENTA)
.map { fadedItalic(it) }
val DIRECTIVE_TYPE = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, DIRECTIVE_COLOR)
val IDENTIFIER_COLOR: TextAttributesKey = DefaultLanguageHighlighterColors.IDENTIFIER
val IDENTIFIER_ATTRIBUTES: TextAttributes = TextAttributes.merge(SCHEME.getAttributes(IDENTIFIER_COLOR), BOLD_ATTRIBUTE).fade()
Expand All @@ -71,6 +80,9 @@ class PreprocessorSyntaxHighlight(private val project: Project) : HighlightVisit

private val doNestedIfIdentWarn get() = PluginSettings.instance.inspectionHighlightNonIndentedNestedIfs
private val doIdentMatchWarn get() = PluginSettings.instance.inspectionHighlightCommentsNotMatchingIfIndents
private val colorNestedIndents get() = PluginSettings.instance.colorNestedPreprocessorComments
private val colorNestedIndentsOnlyIfInLine get() = PluginSettings.instance.colorNestedPreprocessorCommentsOnlyOnSameIndent
private val doSpacingWarn get() = PluginSettings.instance.inspectionRequireJavaKotlinSpacingInConditions

override fun suitableForFile(file: PsiFile): Boolean {
return file.fileType.name.uppercase(Locale.ROOT) in ALLOWED_FILE_TYPES
Expand Down Expand Up @@ -107,8 +119,18 @@ class PreprocessorSyntaxHighlight(private val project: Project) : HighlightVisit
return true
}

fun nonPreprocessorElement(element: PsiElement) {
if (indentStack.isNotEmpty()) {
val indent = indentGetter(element)
if (indentStack.peekFirst() > indent) {
fail(element, "Content of preprocessor block is not indented the same or more than the containing preprocessor comment, this may not get processed correctly!")
}
}
}

override fun visit(element: PsiElement) {
if (element !is PsiCommentImpl) {
nonPreprocessorElement(element)
return
}

Expand Down Expand Up @@ -159,18 +181,16 @@ class PreprocessorSyntaxHighlight(private val project: Project) : HighlightVisit
stack.pop()
}

if (doNestedIfIdentWarn || doIdentMatchWarn) {
val indent = indentGetter(element)
val previousIndent = indentStack.peekFirst()
if (directive == "if") {
if (doNestedIfIdentWarn && indent <= (previousIndent ?: -1)) {
warn(element, "\"$directive\" is not indented more than it's outer \"if\" block (Code clarity)")
}
indentStack.push(indent)
} else if (directive == "elseif") {
if (doIdentMatchWarn && indent != (previousIndent ?: -1)) {
warn(element, "\"$directive\" is not indented the same as it's starting \"if\" (Code clarity)")
}
val indent = indentGetter(element)
val previousIndent = indentStack.peekFirst()
if (directive == "if") {
if (doNestedIfIdentWarn && indent <= (previousIndent ?: -1)) {
warn(element, "\"$directive\" is not indented more than it's outer \"if\" block (Code clarity)")
}
indentStack.push(indent)
} else if (directive == "elseif") {
if (doIdentMatchWarn && indent != (previousIndent ?: -1)) {
warn(element, "\"$directive\" is not indented the same as it's starting \"if\" (Code clarity)")
}
}

Expand All @@ -191,6 +211,8 @@ class PreprocessorSyntaxHighlight(private val project: Project) : HighlightVisit

EXPR_PATTERN.matchEntire(trimmed)?.let { m ->
m.groups[1]?.toHighlight(element, position)?.let(holder::add)
if (doSpacingWarn && !trimmed.contains(" ${m.groups[2]?.value} "))
warn(element, "Operator \"${m.groups[2]?.value}\" should be surrounded by spaces for clarity & consistency with Java / Kotlin styling.")
m.groups[3]?.toHighlight(element, position)?.let(holder::add)
} ?: run {
IDENTIFIER_PATTERN.matchEntire(trimmed)?.let { idMatch ->
Expand All @@ -201,14 +223,15 @@ class PreprocessorSyntaxHighlight(private val project: Project) : HighlightVisit
}

private fun handleIfDef(element: PsiCommentImpl, segments: List<String>, prefixLength: Int) {

val indent = indentGetter(element)
if (doNestedIfIdentWarn) {
val indent = indentGetter(element)
val previousIndent = indentStack.peekFirst()
if (indent <= (previousIndent ?: -1)) {
warn(element, "\"ifdef\" is not indented more than it's outer \"if\" block (Code clarity)")
}
indentStack.push(indent)
}
indentStack.push(indent)

stack.push(Scope.IF)
holder.add("ifdef".toDirectiveHighlight(element, prefixLength))
Expand Down Expand Up @@ -263,11 +286,11 @@ class PreprocessorSyntaxHighlight(private val project: Project) : HighlightVisit
if (indent != (previousIndent ?: -1)) {
warn(element, "\"endif\" is not indented the same as it's starting \"if\" (Code clarity)")
}
indentStack.pop()
} else if (doNestedIfIdentWarn) indentStack.pop()
}

stack.pop()
holder.add("endif".toDirectiveHighlight(element, prefixLength))
indentStack.pop()

if (segments.size > 1) {
fail(element, "\"endif\" should not have arguments")
Expand Down Expand Up @@ -306,6 +329,16 @@ class PreprocessorSyntaxHighlight(private val project: Project) : HighlightVisit
highlightType(element, message, eol, HighlightInfoType.WEAK_WARNING)

private fun highlightType(element: PsiElement, message: String, eol: Boolean = false, type: HighlightInfoType) {
for (i in 0..holder.size() - 1) {
val info = holder.get(i)
if (info.startOffset == element.textRange.startOffset
&& info.description == message
&& info.type == type
) {
return // Avoid repeating the same highlight for each element
}
}

val builder = HighlightInfo.newHighlightInfo(type)
.descriptionAndTooltip(message)

Expand All @@ -320,8 +353,8 @@ class PreprocessorSyntaxHighlight(private val project: Project) : HighlightVisit

private fun MatchGroup.toHighlight(element: PsiCommentImpl, offset: Int): HighlightInfo? {
val trimmed = value.trim()
val type = if (trimmed.toIntOrNull() != null) NUMBER_TYPE else IDENTIFIER_TYPE
val attr = if (trimmed.toIntOrNull() != null) NUMBER_ATTRIBUTES else IDENTIFIER_ATTRIBUTES
val type = if (trimmed.toPreprocessorIntOrNull() != null) NUMBER_TYPE else IDENTIFIER_TYPE
val attr = if (trimmed.toPreprocessorIntOrNull() != null) NUMBER_ATTRIBUTES else IDENTIFIER_ATTRIBUTES
return HighlightInfo.newHighlightInfo(type)
.textAttributes(attr)
.range(
Expand All @@ -333,7 +366,16 @@ class PreprocessorSyntaxHighlight(private val project: Project) : HighlightVisit

private fun String.toDirectiveHighlight(element: PsiCommentImpl, offset: Int): HighlightInfo? =
HighlightInfo.newHighlightInfo(DIRECTIVE_TYPE)
.textAttributes(DIRECTIVE_ATTRIBUTES)
.textAttributes(DIRECTIVE_ATTRIBUTES_NESTED.let {
if (colorNestedIndents) {
val amount = if (colorNestedIndentsOnlyIfInLine) {
indentStack.count { i -> i == indentGetter(element)} - 1
} else {
indentStack.size - 1
}.coerceAtLeast(0)
it[amount % it.size]
} else it[0]
})
.range(element.startOffset + offset, element.startOffset + offset + 1 + length)
.create()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.polyfrost.intelliprocessor.utils
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiFile
import com.intellij.psi.util.startOffset
import org.polyfrost.intelliprocessor.utils.PreprocessorVersion.Companion.toPreprocessorIntOrNull


class PreprocessorConditions private constructor(
Expand Down Expand Up @@ -176,7 +177,7 @@ class PreprocessorConditions private constructor(
val (operator, rhsStr) = match.destructured


val rhs = rhsStr.toIntOrNull()
val rhs = rhsStr.toPreprocessorIntOrNull()
?: return logAndNull("Could not evaluate version number in MC condition: $condition")

val compare = currentVersion.mc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class PreprocessorVersion private constructor(val mc: Int, val loader: String) {
val NULL = PreprocessorVersion(0, "null")

val String.preprocessorVersion: PreprocessorVersion? get() {
val int = makeComparable(this) ?: return null
val int = sugarToInt(this) ?: return null
val loader = split("-").getOrNull(1) ?: return null
return PreprocessorVersion(int, loader)
}
Expand Down Expand Up @@ -53,8 +53,17 @@ class PreprocessorVersion private constructor(val mc: Int, val loader: String) {
return Files.readString(versionFile).trim()
}

/**
* Converts a preprocessor version string to an integer.
* Supports both sugar version format (e.g., `1.16.5`) and integer format (e.g., `11605`).
*/
fun String.toPreprocessorIntOrNull() : Int? =
if (this.contains(".")) sugarToInt(this)
else this.toIntOrNull()


private val regex = "(?<major>\\d+)\\.(?<minor>\\d+)(?:\\.(?<patch>\\d+))?".toRegex()
private fun makeComparable(version: String): Int? {
private fun sugarToInt(version: String): Int? {
val match = regex.find(version) ?: return null
val groups = match.groups

Expand Down
Loading