From 28bdc7ca8170d61060bc24c7d1d7c7ac5bd0b187 Mon Sep 17 00:00:00 2001 From: Aleksandr Nikolaev Date: Mon, 8 Sep 2025 17:41:29 +0200 Subject: [PATCH] Add annotation RequiredByIntellijPlugin to mark API used by Kotlin DataFrame IntelliJ Plugin. Mark such API in the library --- .../jetbrains/kotlinx/dataframe/ColumnsContainer.kt | 3 +++ .../org/jetbrains/kotlinx/dataframe/DataFrame.kt | 3 +++ .../org/jetbrains/kotlinx/dataframe/DataRow.kt | 2 ++ .../annotations/RequiredByIntellijPlugin.kt | 12 ++++++++++++ .../jetbrains/kotlinx/dataframe/api/DataFrameGet.kt | 2 ++ .../jetbrains/kotlinx/dataframe/api/DataRowApi.kt | 4 ++++ .../jetbrains/kotlinx/dataframe/api/generateCode.kt | 3 +++ .../org/jetbrains/kotlinx/dataframe/api/schema.kt | 2 ++ .../jetbrains/kotlinx/dataframe/api/valueCounts.kt | 2 ++ .../kotlinx/dataframe/columns/BaseColumn.kt | 2 ++ .../jetbrains/kotlinx/dataframe/impl/DebugUtil.kt | 3 +++ .../jetbrains/kotlinx/dataframe/impl/Rendering.kt | 2 ++ .../kotlinx/dataframe/impl/columns/DataColumnImpl.kt | 4 ++++ .../dataframe/jupyter/DisableRowsLimitWrapper.kt | 2 ++ .../dataframe/jupyter/KotlinNotebookPluginUtils.kt | 3 +++ .../kotlinx/dataframe/io/JsonFacadeForDebugger.java | 3 +++ 16 files changed, 52 insertions(+) create mode 100644 core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/annotations/RequiredByIntellijPlugin.kt diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/ColumnsContainer.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/ColumnsContainer.kt index 6f58c938ec..02d72ffe43 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/ColumnsContainer.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/ColumnsContainer.kt @@ -1,6 +1,7 @@ package org.jetbrains.kotlinx.dataframe import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.api.ColumnSelectionDsl import org.jetbrains.kotlinx.dataframe.api.asColumnGroup import org.jetbrains.kotlinx.dataframe.api.cast @@ -26,8 +27,10 @@ public interface ColumnsContainer : ColumnsScope { // region columns + @RequiredByIntellijPlugin public fun columns(): List + @RequiredByIntellijPlugin public fun columnsCount(): Int public fun containsColumn(name: String): Boolean diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataFrame.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataFrame.kt index c43bb4ee86..adff782b74 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataFrame.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataFrame.kt @@ -6,6 +6,7 @@ import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload import org.jetbrains.kotlinx.dataframe.annotations.HasSchema import org.jetbrains.kotlinx.dataframe.annotations.Interpretable import org.jetbrains.kotlinx.dataframe.annotations.Refine +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl import org.jetbrains.kotlinx.dataframe.api.add import org.jetbrains.kotlinx.dataframe.api.cast @@ -73,6 +74,7 @@ public interface DataFrame : * * @return The number of rows in the [DataFrame]. */ + @RequiredByIntellijPlugin public fun rowsCount(): Int public operator fun iterator(): Iterator> = rows().iterator() @@ -97,6 +99,7 @@ public interface DataFrame : // region get rows + @RequiredByIntellijPlugin public operator fun get(index: Int): DataRow public operator fun get(indices: Iterable): DataFrame = getRows(indices) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataRow.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataRow.kt index b89437c974..7c0a17c12f 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataRow.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataRow.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlinx.dataframe import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload import org.jetbrains.kotlinx.dataframe.annotations.HasSchema +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.api.next import org.jetbrains.kotlinx.dataframe.api.prev import org.jetbrains.kotlinx.dataframe.columns.ColumnKind @@ -53,6 +54,7 @@ public interface DataRow { public operator fun get(path: ColumnPath): Any? = owner.get(path)[index] + @RequiredByIntellijPlugin public operator fun get(name: String): Any? public fun getColumnGroup(columnName: String): AnyRow { diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/annotations/RequiredByIntellijPlugin.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/annotations/RequiredByIntellijPlugin.kt new file mode 100644 index 0000000000..a4ddcf35ab --- /dev/null +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/annotations/RequiredByIntellijPlugin.kt @@ -0,0 +1,12 @@ +package org.jetbrains.kotlinx.dataframe.annotations + +/** + * Marks API used by Kotlin DataFrame IntelliJ Plugin. + * + * Such API should remain stable: + * - It should not be moved, renamed, or removed. + * - The number of parameters and their types should not change. + * + * If changes to such API are required, they should first be supported in the Kotlin DataFrame IntelliJ Plugin. + */ +internal annotation class RequiredByIntellijPlugin diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataFrameGet.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataFrameGet.kt index 23eca777a0..77bd83cf1e 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataFrameGet.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataFrameGet.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlinx.dataframe.DataColumn import org.jetbrains.kotlinx.dataframe.DataFrame import org.jetbrains.kotlinx.dataframe.DataRow import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup import org.jetbrains.kotlinx.dataframe.columns.ColumnPath import org.jetbrains.kotlinx.dataframe.columns.ColumnReference @@ -167,6 +168,7 @@ public operator fun ColumnsContainer<*>.contains(column: KProperty<*>): Boolean // region rows +@RequiredByIntellijPlugin public fun DataFrame.rows(): Iterable> = object : Iterable> { override fun iterator() = diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataRowApi.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataRowApi.kt index 51e853be21..e3b63b4b48 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataRowApi.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataRowApi.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlinx.dataframe.RowExpression import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload import org.jetbrains.kotlinx.dataframe.annotations.CandidateForRemoval import org.jetbrains.kotlinx.dataframe.annotations.DataSchema +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.columns.ColumnReference import org.jetbrains.kotlinx.dataframe.impl.columnName import org.jetbrains.kotlinx.dataframe.impl.owner @@ -32,6 +33,7 @@ public inline fun AnyRow.valuesOf(): List = values().filterIsInst // region DataSchema @DataSchema +@RequiredByIntellijPlugin public data class NameValuePair(val name: String, val value: V) // Without these overloads row.transpose().name or row.map { name } won't resolve @@ -56,6 +58,7 @@ public val DataRow>.value: Any? public inline fun AnyRow.namedValuesOf(): List> = values().zip(columnNames()).filter { it.first is R }.map { NameValuePair(it.second, it.first as R) } +@RequiredByIntellijPlugin public fun AnyRow.namedValues(): List> = values().zip(columnNames()) { value, name -> NameValuePair(name, value) } @@ -172,6 +175,7 @@ public inline fun DataRow.diffOrNull(expression: RowExpression): public inline fun DataRow.diffOrNull(expression: RowExpression): Float? = prev()?.let { p -> expression(this, this) - expression(p, p) } +@RequiredByIntellijPlugin public fun AnyRow.columnsCount(): Int = df().ncol public fun AnyRow.columnNames(): List = df().columnNames() diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/generateCode.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/generateCode.kt index a9b5380fa1..cfc96719a5 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/generateCode.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/generateCode.kt @@ -3,6 +3,7 @@ package org.jetbrains.kotlinx.dataframe.api import org.jetbrains.kotlinx.dataframe.DataFrame import org.jetbrains.kotlinx.dataframe.annotations.ColumnName import org.jetbrains.kotlinx.dataframe.annotations.DataSchema +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.codeGen.CodeGenerator import org.jetbrains.kotlinx.dataframe.codeGen.MarkerVisibility import org.jetbrains.kotlinx.dataframe.codeGen.NameNormalizer @@ -268,6 +269,7 @@ public val NameNormalizer.Companion.default: NameNormalizer get() = NameNormaliz * @see CodeString.print */ @JvmInline +@RequiredByIntellijPlugin public value class CodeString(public val value: String) { override fun toString(): String = value } @@ -327,6 +329,7 @@ public inline fun DataFrame.generateCode( replaceWith = ReplaceWith(GENERATE_CODE_REPLACE2), level = DeprecationLevel.WARNING, ) +@RequiredByIntellijPlugin public fun DataFrame.generateCode( markerName: String, fields: Boolean = true, diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/schema.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/schema.kt index d8b665bd82..6b058d7446 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/schema.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/schema.kt @@ -3,6 +3,7 @@ package org.jetbrains.kotlinx.dataframe.api import org.jetbrains.kotlinx.dataframe.AnyFrame import org.jetbrains.kotlinx.dataframe.AnyRow import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.impl.api.compileTimeSchemaImpl import org.jetbrains.kotlinx.dataframe.impl.owner import org.jetbrains.kotlinx.dataframe.impl.schema.extractSchema @@ -16,6 +17,7 @@ public fun AnyRow.schema(): DataFrameSchema = owner.schema() // region DataFrame +@RequiredByIntellijPlugin public fun AnyFrame.schema(): DataFrameSchema = extractSchema() // endregion diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/valueCounts.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/valueCounts.kt index d1e7475206..5a49c050a0 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/valueCounts.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/valueCounts.kt @@ -8,6 +8,7 @@ import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload import org.jetbrains.kotlinx.dataframe.annotations.DataSchema import org.jetbrains.kotlinx.dataframe.annotations.Interpretable import org.jetbrains.kotlinx.dataframe.annotations.Refine +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.columns.toColumnSet import org.jetbrains.kotlinx.dataframe.impl.nameGenerator import org.jetbrains.kotlinx.dataframe.util.DEPRECATED_ACCESS_API @@ -28,6 +29,7 @@ public interface ValueCount { internal val defaultCountColumnName: String = ValueCount::count.name +@RequiredByIntellijPlugin public fun DataColumn.valueCounts( sort: Boolean = true, ascending: Boolean = false, diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/columns/BaseColumn.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/columns/BaseColumn.kt index 7a4bf00e3e..2285addb95 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/columns/BaseColumn.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/columns/BaseColumn.kt @@ -4,6 +4,7 @@ import org.jetbrains.kotlinx.dataframe.AnyBaseCol import org.jetbrains.kotlinx.dataframe.AnyCol import org.jetbrains.kotlinx.dataframe.AnyRow import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl import org.jetbrains.kotlinx.dataframe.impl.asList import org.jetbrains.kotlinx.dataframe.impl.columnName @@ -82,6 +83,7 @@ public interface BaseColumn : ColumnReference { public fun values(): Iterable + @RequiredByIntellijPlugin public fun toList(): List = values().asList() public fun toSet(): Set diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DebugUtil.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DebugUtil.kt index 8dff717592..cbaa3f7574 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DebugUtil.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DebugUtil.kt @@ -2,16 +2,19 @@ package org.jetbrains.kotlinx.dataframe.impl import org.jetbrains.kotlinx.dataframe.AnyFrame import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.api.ValueCount import org.jetbrains.kotlinx.dataframe.api.count import org.jetbrains.kotlinx.dataframe.api.map // Needed to attach an expanded node with lazily evaluated expressions to DataFrame debug view @Suppress("unused") +@RequiredByIntellijPlugin internal class Info(val df: AnyFrame) internal class Counts(val value: Any?, val count: Int) { override fun toString(): String = "$value -> $count" } +@RequiredByIntellijPlugin internal fun DataFrame.render(): List = map { Counts(it[0], it.count) } diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/Rendering.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/Rendering.kt index fa382f5454..1771cd49b3 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/Rendering.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/Rendering.kt @@ -4,6 +4,7 @@ import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalTime import org.jetbrains.kotlinx.dataframe.AnyCol import org.jetbrains.kotlinx.dataframe.AnyFrame +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.api.asColumnGroup import org.jetbrains.kotlinx.dataframe.api.asDataFrame import org.jetbrains.kotlinx.dataframe.columns.ColumnKind @@ -54,6 +55,7 @@ internal fun renderType(column: ColumnSchema) = else -> throw NotImplementedError() } +@RequiredByIntellijPlugin internal fun renderType(type: KType?): String { return when (type?.classifier) { null -> "*" diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/DataColumnImpl.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/DataColumnImpl.kt index cdc6eaae77..b511009259 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/DataColumnImpl.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/DataColumnImpl.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlinx.dataframe.impl.columns import org.jetbrains.kotlinx.dataframe.BuildConfig import org.jetbrains.kotlinx.dataframe.DataColumn +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.api.dataFrameOf import org.jetbrains.kotlinx.dataframe.impl.isArray import org.jetbrains.kotlinx.dataframe.impl.isPrimitiveArray @@ -50,10 +51,13 @@ internal abstract class DataColumnImpl( protected val distinct = distinct ?: lazy { values.toSet() } + @RequiredByIntellijPlugin override fun name() = name + @RequiredByIntellijPlugin override fun values() = values + @RequiredByIntellijPlugin override fun type() = type override fun toSet() = distinct.value diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/DisableRowsLimitWrapper.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/DisableRowsLimitWrapper.kt index 68bd3d9abc..715d94a86a 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/DisableRowsLimitWrapper.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/DisableRowsLimitWrapper.kt @@ -1,8 +1,10 @@ package org.jetbrains.kotlinx.dataframe.jupyter import org.jetbrains.kotlinx.dataframe.AnyFrame +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin /** * Allows for disabling the rows limit when generating a DISPLAY output in Jupyter. */ +@RequiredByIntellijPlugin public data class DisableRowsLimitWrapper(public val value: AnyFrame) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/KotlinNotebookPluginUtils.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/KotlinNotebookPluginUtils.kt index de150fd644..238f3712eb 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/KotlinNotebookPluginUtils.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/KotlinNotebookPluginUtils.kt @@ -4,6 +4,7 @@ import org.jetbrains.kotlinx.dataframe.AnyCol import org.jetbrains.kotlinx.dataframe.AnyFrame import org.jetbrains.kotlinx.dataframe.AnyRow import org.jetbrains.kotlinx.dataframe.DataRow +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin import org.jetbrains.kotlinx.dataframe.api.Convert import org.jetbrains.kotlinx.dataframe.api.FormatClause import org.jetbrains.kotlinx.dataframe.api.FormattedFrame @@ -50,6 +51,7 @@ public object KotlinNotebookPluginUtils { * Returns a subset of rows from the given dataframe for rendering. * It's used for example for dynamic pagination in Kotlin Notebook Plugin. */ + @RequiredByIntellijPlugin public fun getRowsSubsetForRendering(dataFrameLike: Any?, startIdx: Int, endIdx: Int): DisableRowsLimitWrapper = when (dataFrameLike) { null -> throw IllegalArgumentException("Dataframe is null") @@ -76,6 +78,7 @@ public object KotlinNotebookPluginUtils { * * @return The sorted dataframe. */ + @RequiredByIntellijPlugin public fun sortByColumns(dataFrameLike: Any?, columnPaths: List>, desc: List): AnyFrame = when (dataFrameLike) { null -> throw IllegalArgumentException("Dataframe is null") diff --git a/dataframe-json/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/JsonFacadeForDebugger.java b/dataframe-json/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/JsonFacadeForDebugger.java index 2e4112daee..a9b6976337 100644 --- a/dataframe-json/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/JsonFacadeForDebugger.java +++ b/dataframe-json/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/JsonFacadeForDebugger.java @@ -1,6 +1,8 @@ package org.jetbrains.kotlinx.dataframe.io; import org.jetbrains.kotlinx.dataframe.DataFrame; +import org.jetbrains.kotlinx.dataframe.annotations.RequiredByIntellijPlugin; + import java.util.Collections; class JsonFacadeForDebugger { @@ -10,6 +12,7 @@ class JsonFacadeForDebugger { * DO NOT BREAK ABI OF THIS METHOD!! * Keep it for backward compatibility, create a new method if signature must change */ + @RequiredByIntellijPlugin static String convertToJson(DataFrame df, int rowLimit, Integer nestedRowLimit) { return JsonKt.toJsonWithMetadata(df, rowLimit, nestedRowLimit, false, Collections.emptyList(), false); }