From 56fbc2a213bcbfdae78190f299443d3f78ea2e8a Mon Sep 17 00:00:00 2001 From: Aleksandr Nikolaev Date: Mon, 8 Sep 2025 17:41:29 +0200 Subject: [PATCH] Add annotation IntellijPluginApi 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 ++ .../dataframe/annotations/IntellijPluginApi.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/IntellijPluginApi.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..fb9197dd93 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.IntellijPluginApi 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 + @IntellijPluginApi public fun columns(): List + @IntellijPluginApi 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..03977e58c1 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataFrame.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataFrame.kt @@ -4,6 +4,7 @@ import org.jetbrains.kotlinx.dataframe.aggregation.Aggregatable import org.jetbrains.kotlinx.dataframe.aggregation.AggregateGroupedBody import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload import org.jetbrains.kotlinx.dataframe.annotations.HasSchema +import org.jetbrains.kotlinx.dataframe.annotations.IntellijPluginApi import org.jetbrains.kotlinx.dataframe.annotations.Interpretable import org.jetbrains.kotlinx.dataframe.annotations.Refine import org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl @@ -73,6 +74,7 @@ public interface DataFrame : * * @return The number of rows in the [DataFrame]. */ + @IntellijPluginApi public fun rowsCount(): Int public operator fun iterator(): Iterator> = rows().iterator() @@ -97,6 +99,7 @@ public interface DataFrame : // region get rows + @IntellijPluginApi 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..6284009703 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.IntellijPluginApi 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] + @IntellijPluginApi 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/IntellijPluginApi.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/annotations/IntellijPluginApi.kt new file mode 100644 index 0000000000..a8c9534a15 --- /dev/null +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/annotations/IntellijPluginApi.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 IntellijPluginApi 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..0bc9ec956f 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.IntellijPluginApi 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 +@IntellijPluginApi 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..be517ccb70 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.IntellijPluginApi 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 +@IntellijPluginApi 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) } +@IntellijPluginApi 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) } +@IntellijPluginApi 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..8a9f62456a 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.IntellijPluginApi 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 +@IntellijPluginApi 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, ) +@IntellijPluginApi 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..6d8df934e4 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.IntellijPluginApi 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 +@IntellijPluginApi 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..93146a6c52 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 @@ -6,6 +6,7 @@ import org.jetbrains.kotlinx.dataframe.DataColumn import org.jetbrains.kotlinx.dataframe.DataFrame import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload import org.jetbrains.kotlinx.dataframe.annotations.DataSchema +import org.jetbrains.kotlinx.dataframe.annotations.IntellijPluginApi import org.jetbrains.kotlinx.dataframe.annotations.Interpretable import org.jetbrains.kotlinx.dataframe.annotations.Refine import org.jetbrains.kotlinx.dataframe.columns.toColumnSet @@ -28,6 +29,7 @@ public interface ValueCount { internal val defaultCountColumnName: String = ValueCount::count.name +@IntellijPluginApi 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..31aea0c760 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.IntellijPluginApi 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 + @IntellijPluginApi 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..18832cce48 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.IntellijPluginApi 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") +@IntellijPluginApi internal class Info(val df: AnyFrame) internal class Counts(val value: Any?, val count: Int) { override fun toString(): String = "$value -> $count" } +@IntellijPluginApi 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..24d7636e0c 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.IntellijPluginApi 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() } +@IntellijPluginApi 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..b9b7cfca93 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.IntellijPluginApi 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() } + @IntellijPluginApi override fun name() = name + @IntellijPluginApi override fun values() = values + @IntellijPluginApi 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..723dfd5a4d 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.IntellijPluginApi /** * Allows for disabling the rows limit when generating a DISPLAY output in Jupyter. */ +@IntellijPluginApi 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..86b6e4adec 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.IntellijPluginApi 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. */ + @IntellijPluginApi 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. */ + @IntellijPluginApi 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..c03d89a5d8 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.IntellijPluginApi; + 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 */ + @IntellijPluginApi static String convertToJson(DataFrame df, int rowLimit, Integer nestedRowLimit) { return JsonKt.toJsonWithMetadata(df, rowLimit, nestedRowLimit, false, Collections.emptyList(), false); }