Skip to content

Commit 76f1d75

Browse files
Fix HTML table report; encapsulate logic about cell variables
1 parent 3fd2eb3 commit 76f1d75

File tree

5 files changed

+65
-60
lines changed

5 files changed

+65
-60
lines changed

src/main/kotlin/org/jetbrains/kotlinx/jupyter/htmlUtil.kt

+9-19
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,19 @@ package org.jetbrains.kotlinx.jupyter
22

33
import org.jetbrains.kotlinx.jupyter.api.VariableState
44

5+
const val varsTableStyleClass = "variables_table"
6+
57
// TODO : perhaps, create factory class
68
fun generateHTMLVarsReport(variablesState: Map<String, VariableState>): String {
79
return buildString {
8-
append(
9-
"""
10-
<!DOCTYPE html>
11-
<html>
12-
<head>
13-
14-
""".trimIndent()
15-
)
1610
append(generateStyleSection())
17-
append("\n</head>\n<body>\n")
18-
append("<h2 style=\"text-align:center\">Variables State</h2>\n")
19-
2011
if (variablesState.isEmpty()) {
21-
this.append("<p>Empty state</p>\n\n")
22-
this.append("</body>\n</html>")
23-
return this.toString()
12+
append("<h2 style=\"text-align:center;\">Variables State's Empty</h2>\n")
13+
return toString()
2414
}
2515

16+
append("<h2 style=\"text-align:center;\">Variables State</h2>\n")
2617
append(generateVarsTable(variablesState))
27-
28-
append("</body>\n</html>")
2918
}
3019
}
3120

@@ -34,15 +23,16 @@ fun generateStyleSection(borderPx: Int = 1, paddingPx: Int = 5): String {
3423
//language=HTML
3524
val styleSection = """
3625
<style>
37-
table, th, td {
26+
table.$varsTableStyleClass, .$varsTableStyleClass th, .$varsTableStyleClass td {
3827
border: ${borderPx}px solid black;
3928
border-collapse: collapse;
4029
text-align:center;
4130
}
42-
th, td {
31+
.$varsTableStyleClass th, .$varsTableStyleClass td {
4332
padding: ${paddingPx}px;
4433
}
4534
</style>
35+
4636
""".trimIndent()
4737
return styleSection
4838
}
@@ -51,7 +41,7 @@ fun generateVarsTable(variablesState: Map<String, VariableState>): String {
5141
return buildString {
5242
append(
5343
"""
54-
<table style="width:80%" align="center">
44+
<table class="$varsTableStyleClass" style="width:80%;margin-left:auto;margin-right:auto;" align="center">
5545
<tr>
5646
<th>Variable</th>
5747
<th>Value</th>

src/main/kotlin/org/jetbrains/kotlinx/jupyter/repl/impl/InternalEvaluatorImpl.kt

+10-29
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.jetbrains.kotlinx.jupyter.repl.impl
22

33
import kotlinx.coroutines.runBlocking
44
import org.jetbrains.kotlinx.jupyter.ReplEvalRuntimeException
5+
import org.jetbrains.kotlinx.jupyter.VariablesUsagesPerCellWatcher
56
import org.jetbrains.kotlinx.jupyter.api.Code
67
import org.jetbrains.kotlinx.jupyter.api.FieldValue
78
import org.jetbrains.kotlinx.jupyter.api.VariableState
@@ -64,12 +65,10 @@ internal class InternalEvaluatorImpl(
6465

6566
override val variablesHolder = mutableMapOf<String, VariableState>()
6667

67-
override val cellVariables = mutableMapOf<Int, MutableSet<String>>()
68+
override val cellVariables: Map<Int, Set<String>>
69+
get() = variablesWatcher.cellVariables
6870

69-
/**
70-
* Tells in which cell a variable was declared
71-
*/
72-
private val varsDeclarationInfo: MutableMap<String, Int> = mutableMapOf()
71+
private val variablesWatcher: VariablesUsagesPerCellWatcher<Int, String> = VariablesUsagesPerCellWatcher()
7372

7473
private var isExecuting = false
7574

@@ -143,18 +142,15 @@ internal class InternalEvaluatorImpl(
143142
}
144143

145144
private fun updateVariablesState(cellId: Int) {
146-
// remove known modifying usages in this cell
147-
cellVariables[cellId]?.removeIf {
148-
varsDeclarationInfo[it] != cellId
149-
}
145+
variablesWatcher.removeOldUsages(cellId)
150146

151147
variablesHolder.forEach {
152148
val state = it.value as VariableStateImpl
153149
val oldValue = state.stringValue
154150
state.update()
155151

156152
if (state.stringValue != oldValue) {
157-
cellVariables[cellId]?.add(it.key)
153+
variablesWatcher.addUsage(cellId, it.key)
158154
}
159155
}
160156
}
@@ -168,21 +164,13 @@ internal class InternalEvaluatorImpl(
168164
fields.forEach { property ->
169165
property as KProperty1<Any, *>
170166
val state = VariableStateImpl(property, cellClassInstance)
167+
variablesWatcher.addDeclaration(cellId, property.name)
171168

172-
// redeclaration of any type
173-
if (varsDeclarationInfo.containsKey(property.name)) {
174-
val oldCellId = varsDeclarationInfo[property.name]
175-
if (oldCellId != cellId) {
176-
cellVariables[oldCellId]?.remove(property.name)
177-
}
178-
}
179169
// it was val, now it's var
180170
if (property is KMutableProperty1) {
181171
variablesHolder.remove(property.name)
182172
}
183-
varsDeclarationInfo[property.name] = cellId
184-
cellVariables[cellId]?.add(property.name)
185-
// invariant with changes: cache --> varsMap, other way is not allowed
173+
186174
if (property !is KMutableProperty1) {
187175
variablesHolder[property.name] = state
188176
return@forEach
@@ -193,15 +181,8 @@ internal class InternalEvaluatorImpl(
193181
}
194182

195183
private fun updateDataAfterExecution(lastExecutionCellId: Int, resultValue: ResultValue) {
196-
cellVariables.putIfAbsent(lastExecutionCellId, mutableSetOf())
197-
198-
val visibleDeclarations = getVisibleVariables(resultValue, lastExecutionCellId)
199-
visibleDeclarations.forEach {
200-
val cellSet = cellVariables[lastExecutionCellId] ?: return@forEach
201-
varsDeclarationInfo[it.key] = lastExecutionCellId
202-
cellSet += it.key
203-
}
204-
variablesHolder += visibleDeclarations
184+
variablesWatcher.ensureStorageCreation(lastExecutionCellId)
185+
variablesHolder += getVisibleVariables(resultValue, lastExecutionCellId)
205186

206187
updateVariablesState(lastExecutionCellId)
207188
}

src/main/kotlin/org/jetbrains/kotlinx/jupyter/util.kt

+40
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,43 @@ fun Int.toSourceCodePositionWithNewAbsolute(code: SourceCode, newCode: SourceCod
6868
fun ResultsRenderersProcessor.registerDefaultRenderers() {
6969
register(bufferedImageRenderer)
7070
}
71+
72+
/**
73+
* Stores info about where a variable Y was declared and info about what are they at the address X.
74+
* T: key, stands for a way of addressing variables, e.g. address.
75+
* V: value, from Variable, choose any suitable type for your variable reference.
76+
* Default: Int, String
77+
*/
78+
class VariablesUsagesPerCellWatcher<T : Any, U : Any> {
79+
val cellVariables = mutableMapOf<T, MutableSet<U>>()
80+
81+
/**
82+
* Tells in which cell a variable was declared
83+
*/
84+
private val variablesDeclarationInfo: MutableMap<U, T> = mutableMapOf()
85+
86+
fun addDeclaration(address: T, variableRef: U) {
87+
ensureStorageCreation(address)
88+
89+
// redeclaration of any type
90+
if (variablesDeclarationInfo.containsKey(variableRef)) {
91+
val oldCellId = variablesDeclarationInfo[variableRef]
92+
if (oldCellId != address) {
93+
cellVariables[oldCellId]?.remove(variableRef)
94+
}
95+
}
96+
variablesDeclarationInfo[variableRef] = address
97+
cellVariables[address]?.add(variableRef)
98+
}
99+
100+
fun addUsage(address: T, variableRef: U) = cellVariables[address]?.add(variableRef)
101+
102+
fun removeOldUsages(newAddress: T) {
103+
// remove known modifying usages in this cell
104+
cellVariables[newAddress]?.removeIf {
105+
variablesDeclarationInfo[it] != newAddress
106+
}
107+
}
108+
109+
fun ensureStorageCreation(address: T) = cellVariables.putIfAbsent(address, mutableSetOf())
110+
}

src/test/kotlin/org/jetbrains/kotlinx/jupyter/test/ApiTest.kt

+6-11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.jetbrains.kotlinx.jupyter.test
33
import org.jetbrains.kotlinx.jupyter.EvalResult
44
import org.jetbrains.kotlinx.jupyter.generateHTMLVarsReport
55
import org.jetbrains.kotlinx.jupyter.repl.impl.getSimpleCompiler
6+
import org.jetbrains.kotlinx.jupyter.varsTableStyleClass
67
import org.jetbrains.kotlinx.jupyter.test.repl.AbstractSingleReplTest
78
import org.junit.jupiter.api.Test
89
import kotlin.script.experimental.api.ScriptCompilationConfiguration
@@ -62,23 +63,18 @@ class ApiTest : AbstractSingleReplTest() {
6263
val htmlText = generateHTMLVarsReport(repl.notebook.variablesState)
6364
assertEquals(
6465
"""
65-
<!DOCTYPE html>
66-
<html>
67-
<head>
6866
<style>
69-
table, th, td {
67+
table.$varsTableStyleClass, .$varsTableStyleClass th, .$varsTableStyleClass td {
7068
border: 1px solid black;
7169
border-collapse: collapse;
7270
text-align:center;
7371
}
74-
th, td {
72+
.$varsTableStyleClass th, .$varsTableStyleClass td {
7573
padding: 5px;
7674
}
7775
</style>
78-
</head>
79-
<body>
80-
<h2 style="text-align:center">Variables State</h2>
81-
<table style="width:80%" align="center">
76+
<h2 style="text-align:center;">Variables State</h2>
77+
<table class="$varsTableStyleClass" style="width:80%;margin-left:auto;margin-right:auto;" align="center">
8278
<tr>
8379
<th>Variable</th>
8480
<th>Value</th>
@@ -94,8 +90,7 @@ class ApiTest : AbstractSingleReplTest() {
9490
<td>47</td>
9591
</tr>
9692
</table>
97-
</body>
98-
</html>
93+
9994
""".trimIndent(),
10095
htmlText
10196
)

src/test/kotlin/org/jetbrains/kotlinx/jupyter/test/repl/ReplTests.kt

-1
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,6 @@ class ReplVarsTest : AbstractSingleReplTest() {
571571
assertEquals("1", varsState["z"]!!.stringValue)
572572
}
573573

574-
575574
@Test
576575
fun testPrivateVarsCaptureSeparateCells() {
577576
eval(

0 commit comments

Comments
 (0)