package org.fxmisc.richtext;

import java.util.function.IntFunction;

import javafx.scene.Node;
import javafx.scene.control.Label;

import org.reactfx.EventStream;
import org.reactfx.EventStreams;

public class LineNumberFactory implements IntFunction<Node> {

    private static final String STYLESHEET = LineNumberFactory.class.getResource("lineno.css").toExternalForm();

    public static IntFunction<Node> get(StyledTextArea<?> area,String customStylesheet) {
        return new LineNumberFactory(area,customStylesheet);
    }
    public static IntFunction<Node> get(StyledTextArea<?> area) {
        return new LineNumberFactory(area, STYLESHEET);
    }

    public static IntFunction<Node> get(StyledTextArea<?> area,IntFunction<String> format) {
        return new LineNumberFactory(area,format,STYLESHEET);
    }
    public static IntFunction<Node> get(StyledTextArea<?> area,IntFunction<String> format,String customStylesheet) {
        return new LineNumberFactory(area,format,customStylesheet);
    }
    private final EventStream<Integer> nParagraphs;
    private final String stylesheet;
    private final IntFunction<String> format;

    private LineNumberFactory(StyledTextArea<?> area, String stylesheet) {
        nParagraphs = EventStreams.sizeOf(area.getParagraphs());
        this.format = (digits -> "%0" + digits + "d");
        this.stylesheet = stylesheet;
    }
    private LineNumberFactory(StyledTextArea<?> area, IntFunction<String> format, String stylesheet) {
        nParagraphs = EventStreams.sizeOf(area.getParagraphs());
        this.format = format;
        this.stylesheet = stylesheet;
    }

    @Override
    public Node apply(int idx) {
        Label lineNo = new Label();
        lineNo.getStyleClass().add("lineno");
        lineNo.getStylesheets().add(stylesheet);

        // When removed from the scene, stay subscribed to never(), which is
        // a fake subscription that consumes no resources, instead of staying
        // subscribed to area's paragraphs.
        EventStreams.valuesOf(lineNo.sceneProperty())
                .flatMap(scene -> scene != null
                        ? nParagraphs.map(n -> format(idx+1, n))
                        : EventStreams.<String>never())
                .feedTo(lineNo.textProperty());
        return lineNo;
    }

    private String format(int x, int max) {
        int digits = (int) Math.floor(Math.log10(max)) + 1;
        return String.format(format.apply(digits), x);
    }
}

@@ -144,27 +144,35 @@
 reactfx.
-EventStream
+collection.LiveList
 ;%0Aimport
@@ -184,28 +184,25 @@
 reactfx.
-EventStreams
+value.Val
 ;%0A%0Apubli
@@ -1029,27 +1029,19 @@
 e final 
-EventStream
+Val
 %3CInteger
@@ -1232,36 +1232,32 @@
 aragraphs = 
-EventStreams
+LiveList
 .sizeOf(area
@@ -1499,28 +1499,24 @@
 raphs = 
-EventStreams
+LiveList
 .sizeOf(
@@ -1836,44 +1836,47 @@
 ne, 
-stay subscribed to never()
+bind label's text to constant %22%22
 , which
- is
 %0A   
@@ -1886,16 +1886,19 @@
   //
+ is
  a fake 
 subs
@@ -1897,20 +1897,15 @@
 ake 
-subscription
+binding
  tha
@@ -1963,17 +1963,12 @@
  // 
-subscribe
+boun
 d to
@@ -1999,30 +1999,64 @@
     
-EventStreams.valuesOf(
+lineNo.textProperty().bind(Val.flatMap(%0A                
 line
@@ -2073,17 +2073,17 @@
 operty()
-)
+,
 %0A       
@@ -2091,25 +2091,16 @@
         
-.flatMap(
 scene -%3E
@@ -2209,82 +2209,24 @@
   : 
-EventStreams.%3CString%3Enever())%0A                .feedTo(lineNo.textProperty(
+Val.constant(%22%22)
 ));%0A

Use Vals instead of EventStreams in LineNumberFactory.