Gutters¶
The gutter system displays markers (line numbers, breakpoint indicators, etc.) alongside the editor content.
Line numbers¶
The simplest gutter is the built-in line number gutter:
import com.monkopedia.kodemirror.view.lineNumbers
// Add to your extensions:
basicSetup + lineNumbers
lineNumbers is a top-level Extension property — just add it to your extensions list.
Custom gutter markers¶
Create a custom gutter by implementing GutterMarker and providing a GutterConfig:
import com.monkopedia.kodemirror.view.*
class BreakpointMarker : GutterMarker() {
@Composable
override fun Content(theme: EditorTheme) {
Text("●", color = Color.Red)
}
}
val breakpointGutter = gutter(GutterConfig(
cssClass = "cm-breakpoints",
lineMarker = { view, lineFrom ->
if (isBreakpointLine(view.state, lineFrom)) BreakpointMarker()
else null
}
))
The lineMarker callback receives the EditorSession and the character offset of the line start (line.from), not the line number. Return a GutterMarker to display something, or null for no marker.
GutterMarker¶
GutterMarker is an abstract class with a Compose Content() method (replacing toDOM() in upstream CodeMirror):
abstract class GutterMarker : RangeValue() {
@Composable
abstract fun Content(theme: EditorTheme)
open val elementClass: String? get() = null
}
GutterConfig¶
| Property | Type | Description |
|---|---|---|
cssClass | String? | CSS class for the gutter column |
lineMarker | (EditorSession, Int) -> GutterMarker? | Called per line with (view, lineFrom) |
lineMarkerChange | (ViewUpdate) -> Boolean | When to re-call lineMarker |
renderEmptyElements | Boolean | Render markers even when null |
initialSpacer | (EditorSession) -> GutterMarker | Spacer for initial width measurement |
updateSpacer | (GutterMarker, ViewUpdate) -> GutterMarker | Update the spacer |
Tracking breakpoints with a StateField¶
A complete pattern storing breakpoints in a StateField:
private val toggleBreakpoint = StateEffect.define<LineNumber>()
private class BreakpointMarker : GutterMarker() {
@Composable
override fun Content(theme: EditorTheme) {
Box(
modifier = Modifier
.size(12.dp)
.clip(CircleShape)
.background(Color(0xFFE06C75))
)
}
}
private val breakpointState: StateField<Set<LineNumber>> = StateField.define(
StateFieldSpec(
create = { emptySet() },
update = { value, tr ->
var result = value
for (effect in tr.effects) {
val e = effect.asType(toggleBreakpoint)
if (e != null) {
val line = e.value
result = if (line in result) result - line else result + line
}
}
result
}
)
)
private val breakpointGutter = gutter(
GutterConfig(
type = GutterType.Custom("breakpoints"),
lineMarker = { session, lineFrom ->
val breakpoints = session.state.field(breakpointState)
val lineNumber = session.state.doc.lineAt(DocPos(lineFrom)).number
if (lineNumber in breakpoints) BreakpointMarker() else null
},
lineMarkerChange = { update ->
update.transactions.any { tr ->
tr.effects.any { it.asType(toggleBreakpoint) != null }
}
}
)
)
Toggle a breakpoint by dispatching:
Active line gutter¶
Highlight the gutter for the current line:
import com.monkopedia.kodemirror.view.highlightActiveLineGutter
lineNumbers + highlightActiveLineGutter + // ...
Related API¶
GutterConfig— gutter configurationlineNumbers— line number gutter extensiongutter— custom gutter extensionGutterMarker— base class for gutter markers
Based on the CodeMirror Gutter example.