From 083b4b994ecdc9b18bc24e7e9aa25b7ba19bd3e6 Mon Sep 17 00:00:00 2001 From: Jurgen <5031427+Jugen@users.noreply.github.com> Date: Wed, 21 May 2025 17:53:32 +0200 Subject: [PATCH 1/4] Add getVisibleParagraphIndexes method and use in JavaKeywordsDemo --- .../richtext/demo/JavaKeywordsDemo.java | 33 ++++++++----------- .../fxmisc/richtext/GenericStyledArea.java | 8 ++++- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java b/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java index 61968939..d77a3123 100644 --- a/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java +++ b/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java @@ -3,10 +3,12 @@ import java.time.Duration; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javafx.application.Application; import javafx.application.Platform; @@ -27,6 +29,7 @@ import org.fxmisc.richtext.model.StyleSpansBuilder; import org.reactfx.collection.ListModification; import org.reactfx.Subscription; +import org.reactfx.util.Tuple2; public class JavaKeywordsDemo extends Application { @@ -120,7 +123,7 @@ public void start(Stage primaryStage) { // recompute syntax highlighting only for visible paragraph changes // Note that this shows how it can be done but is not recommended for production where multi- // line syntax requirements are needed, like comment blocks without a leading * on each line. - codeArea.getVisibleParagraphs().addModificationObserver + codeArea.getVisibleParagraphIndexes().addModificationObserver ( new VisibleParagraphStyler<>( codeArea, this::computeHighlighting ) ); @@ -170,11 +173,10 @@ private StyleSpans> computeHighlighting(String text) { return spansBuilder.create(); } - private class VisibleParagraphStyler implements Consumer>> + private class VisibleParagraphStyler implements Consumer>>> { private final GenericStyledArea area; private final Function> computeStyles; - private int prevParagraph, prevTextLength; public VisibleParagraphStyler( GenericStyledArea area, Function> computeStyles ) { @@ -183,24 +185,15 @@ public VisibleParagraphStyler( GenericStyledArea area, Function> lm ) + public void accept( ListModification>> lm ) { - if ( lm.getAddedSize() > 0 ) Platform.runLater( () -> - { - int paragraph = Math.min( area.firstVisibleParToAllParIndex() + lm.getFrom(), area.getParagraphs().size()-1 ); - String text = area.getText( paragraph, 0, paragraph, area.getParagraphLength( paragraph ) ); - - if ( paragraph != prevParagraph || text.length() != prevTextLength ) - { - if ( paragraph < area.getParagraphs().size()-1 ) - { - int startPos = area.getAbsolutePosition( paragraph, 0 ); - area.setStyleSpans( startPos, computeStyles.apply( text ) ); - } - prevTextLength = text.length(); - prevParagraph = paragraph; - } - }); + if ( lm.getAddedSize() == 0 ) return; + + List>> addList = lm.getAddedSubList(); + String text = addList.stream().map( t2 -> t2.get2().getText() ).collect( Collectors.joining( "\n" ) ); + int startPos = area.getAbsolutePosition( addList.get(0).get1(), 0 ); + + Platform.runLater( () -> area.setStyleSpans( startPos, computeStyles.apply( text ) ) ); } } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java index 61453458..2eb4c329 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java @@ -94,6 +94,7 @@ import org.reactfx.collection.LiveList; import org.reactfx.collection.SuspendableList; import org.reactfx.util.Tuple2; +import org.reactfx.util.Tuples; import org.reactfx.value.Val; import org.reactfx.value.Var; @@ -569,6 +570,9 @@ public final boolean removeSelection(Selection selection) { // paragraphs @Override public LiveList> getParagraphs() { return content.getParagraphs(); } + private final SuspendableList>> visibleParagraphIndexes; + public final LiveList>> getVisibleParagraphIndexes() { return visibleParagraphIndexes; } + private final SuspendableList> visibleParagraphs; @Override public final LiveList> getVisibleParagraphs() { return visibleParagraphs; } @@ -816,7 +820,9 @@ public GenericStyledArea( caretSet.add(caretSelectionBind.getUnderlyingCaret()); selectionSet.add(caretSelectionBind.getUnderlyingSelection()); - visibleParagraphs = LiveList.map(virtualFlow.visibleCells(), c -> c.getNode().getParagraph()).suspendable(); + LiveList> visibleNodes = LiveList.map(virtualFlow.visibleCells(), c -> c.getNode()); + visibleParagraphIndexes = visibleNodes.map(pb -> Tuples.t(pb.getIndex(), pb.getParagraph())).suspendable(); + visibleParagraphs = visibleNodes.map(pb -> pb.getParagraph()).suspendable(); final Suspendable omniSuspendable = Suspendable.combine( beingUpdated, // must be first, to be the last one to release From 1d938a18dcfe71f7c80c96d49cc278f70e158718 Mon Sep 17 00:00:00 2001 From: Jurgen <5031427+Jugen@users.noreply.github.com> Date: Mon, 26 May 2025 11:00:54 +0200 Subject: [PATCH 2/4] Revert "Add getVisibleParagraphIndexes method" This reverts commit 083b4b994ecdc9b18bc24e7e9aa25b7ba19bd3e6. --- .../richtext/demo/JavaKeywordsDemo.java | 33 +++++++++++-------- .../fxmisc/richtext/GenericStyledArea.java | 8 +---- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java b/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java index d77a3123..61968939 100644 --- a/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java +++ b/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java @@ -3,12 +3,10 @@ import java.time.Duration; import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import javafx.application.Application; import javafx.application.Platform; @@ -29,7 +27,6 @@ import org.fxmisc.richtext.model.StyleSpansBuilder; import org.reactfx.collection.ListModification; import org.reactfx.Subscription; -import org.reactfx.util.Tuple2; public class JavaKeywordsDemo extends Application { @@ -123,7 +120,7 @@ public void start(Stage primaryStage) { // recompute syntax highlighting only for visible paragraph changes // Note that this shows how it can be done but is not recommended for production where multi- // line syntax requirements are needed, like comment blocks without a leading * on each line. - codeArea.getVisibleParagraphIndexes().addModificationObserver + codeArea.getVisibleParagraphs().addModificationObserver ( new VisibleParagraphStyler<>( codeArea, this::computeHighlighting ) ); @@ -173,10 +170,11 @@ private StyleSpans> computeHighlighting(String text) { return spansBuilder.create(); } - private class VisibleParagraphStyler implements Consumer>>> + private class VisibleParagraphStyler implements Consumer>> { private final GenericStyledArea area; private final Function> computeStyles; + private int prevParagraph, prevTextLength; public VisibleParagraphStyler( GenericStyledArea area, Function> computeStyles ) { @@ -185,15 +183,24 @@ public VisibleParagraphStyler( GenericStyledArea area, Function>> lm ) + public void accept( ListModification> lm ) { - if ( lm.getAddedSize() == 0 ) return; - - List>> addList = lm.getAddedSubList(); - String text = addList.stream().map( t2 -> t2.get2().getText() ).collect( Collectors.joining( "\n" ) ); - int startPos = area.getAbsolutePosition( addList.get(0).get1(), 0 ); - - Platform.runLater( () -> area.setStyleSpans( startPos, computeStyles.apply( text ) ) ); + if ( lm.getAddedSize() > 0 ) Platform.runLater( () -> + { + int paragraph = Math.min( area.firstVisibleParToAllParIndex() + lm.getFrom(), area.getParagraphs().size()-1 ); + String text = area.getText( paragraph, 0, paragraph, area.getParagraphLength( paragraph ) ); + + if ( paragraph != prevParagraph || text.length() != prevTextLength ) + { + if ( paragraph < area.getParagraphs().size()-1 ) + { + int startPos = area.getAbsolutePosition( paragraph, 0 ); + area.setStyleSpans( startPos, computeStyles.apply( text ) ); + } + prevTextLength = text.length(); + prevParagraph = paragraph; + } + }); } } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java index 2eb4c329..61453458 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java @@ -94,7 +94,6 @@ import org.reactfx.collection.LiveList; import org.reactfx.collection.SuspendableList; import org.reactfx.util.Tuple2; -import org.reactfx.util.Tuples; import org.reactfx.value.Val; import org.reactfx.value.Var; @@ -570,9 +569,6 @@ public final boolean removeSelection(Selection selection) { // paragraphs @Override public LiveList> getParagraphs() { return content.getParagraphs(); } - private final SuspendableList>> visibleParagraphIndexes; - public final LiveList>> getVisibleParagraphIndexes() { return visibleParagraphIndexes; } - private final SuspendableList> visibleParagraphs; @Override public final LiveList> getVisibleParagraphs() { return visibleParagraphs; } @@ -820,9 +816,7 @@ public GenericStyledArea( caretSet.add(caretSelectionBind.getUnderlyingCaret()); selectionSet.add(caretSelectionBind.getUnderlyingSelection()); - LiveList> visibleNodes = LiveList.map(virtualFlow.visibleCells(), c -> c.getNode()); - visibleParagraphIndexes = visibleNodes.map(pb -> Tuples.t(pb.getIndex(), pb.getParagraph())).suspendable(); - visibleParagraphs = visibleNodes.map(pb -> pb.getParagraph()).suspendable(); + visibleParagraphs = LiveList.map(virtualFlow.visibleCells(), c -> c.getNode().getParagraph()).suspendable(); final Suspendable omniSuspendable = Suspendable.combine( beingUpdated, // must be first, to be the last one to release From 58e577172ea348b9e5295cc45adf6149c724d95f Mon Sep 17 00:00:00 2001 From: Jurgen <5031427+Jugen@users.noreply.github.com> Date: Mon, 26 May 2025 12:45:38 +0200 Subject: [PATCH 3/4] Add setVisibleOnlyStyler method --- .../richtext/demo/JavaKeywordsDemo.java | 46 +------------------ .../fxmisc/richtext/GenericStyledArea.java | 15 ++++++ 2 files changed, 16 insertions(+), 45 deletions(-) diff --git a/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java b/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java index 61968939..24552256 100644 --- a/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java +++ b/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java @@ -1,10 +1,7 @@ package org.fxmisc.richtext.demo; -import java.time.Duration; import java.util.Collection; import java.util.Collections; -import java.util.function.Consumer; -import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -20,13 +17,9 @@ import org.fxmisc.flowless.VirtualizedScrollPane; import org.fxmisc.richtext.CodeArea; -import org.fxmisc.richtext.GenericStyledArea; import org.fxmisc.richtext.LineNumberFactory; -import org.fxmisc.richtext.model.Paragraph; import org.fxmisc.richtext.model.StyleSpans; import org.fxmisc.richtext.model.StyleSpansBuilder; -import org.reactfx.collection.ListModification; -import org.reactfx.Subscription; public class JavaKeywordsDemo extends Application { @@ -120,10 +113,7 @@ public void start(Stage primaryStage) { // recompute syntax highlighting only for visible paragraph changes // Note that this shows how it can be done but is not recommended for production where multi- // line syntax requirements are needed, like comment blocks without a leading * on each line. - codeArea.getVisibleParagraphs().addModificationObserver - ( - new VisibleParagraphStyler<>( codeArea, this::computeHighlighting ) - ); + codeArea.setVisibleOnlyStyler( p -> p.restyle( 0, computeHighlighting( p.getText() ) ) ); // auto-indent: insert previous line's indents on enter final Pattern whiteSpace = Pattern.compile( "^\\s+" ); @@ -170,40 +160,6 @@ private StyleSpans> computeHighlighting(String text) { return spansBuilder.create(); } - private class VisibleParagraphStyler implements Consumer>> - { - private final GenericStyledArea area; - private final Function> computeStyles; - private int prevParagraph, prevTextLength; - - public VisibleParagraphStyler( GenericStyledArea area, Function> computeStyles ) - { - this.computeStyles = computeStyles; - this.area = area; - } - - @Override - public void accept( ListModification> lm ) - { - if ( lm.getAddedSize() > 0 ) Platform.runLater( () -> - { - int paragraph = Math.min( area.firstVisibleParToAllParIndex() + lm.getFrom(), area.getParagraphs().size()-1 ); - String text = area.getText( paragraph, 0, paragraph, area.getParagraphLength( paragraph ) ); - - if ( paragraph != prevParagraph || text.length() != prevTextLength ) - { - if ( paragraph < area.getParagraphs().size()-1 ) - { - int startPos = area.getAbsolutePosition( paragraph, 0 ); - area.setStyleSpans( startPos, computeStyles.apply( text ) ); - } - prevTextLength = text.length(); - prevParagraph = paragraph; - } - }); - } - } - private class DefaultContextMenu extends ContextMenu { private MenuItem fold, unfold, print; diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java index 61453458..a83f3fc9 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java @@ -1548,6 +1548,20 @@ public final PS getParagraphStyleForInsertionAt(int pos) { } } + private UnaryOperator> visibleOnlyStyler; + /** + * This styler will only be applied to Paragraphs, just before being displayed. + *

Important Notes

+ *
    + *
  1. The result of this styling does NOT modify the document model. + *
  2. Paragraph is immutable, so don't return the same object expecting changes. + *
  3. The styler should return the result of one of Paragraph's restyle methods. + *
+ */ + public void setVisibleOnlyStyler(UnaryOperator> styler) { + visibleOnlyStyler = styler; + } + @Override public void replaceText(int start, int end, String text) { StyledDocument doc = ReadOnlyStyledDocument.fromString( @@ -1914,6 +1928,7 @@ private Cell, ParagraphBox> createCell( BiConsumer applyParagraphStyle, Function, Node> nodeFactory) { + if (visibleOnlyStyler != null) paragraph = visibleOnlyStyler.apply(paragraph); ParagraphBox box = new ParagraphBox<>(paragraph, applyParagraphStyle, nodeFactory); box.highlightTextFillProperty().bind(highlightTextFill); From 1163c1ef9509f8ae561b27a3be66a1a55650895c Mon Sep 17 00:00:00 2001 From: Jurgen <5031427+Jugen@users.noreply.github.com> Date: Thu, 14 Aug 2025 16:41:41 +0200 Subject: [PATCH 4/4] Updated setVisibleOnlyStyler to take BiFunction and added refreshParagraphs --- .../fxmisc/richtext/demo/JavaKeywordsDemo.java | 9 +++------ .../org/fxmisc/richtext/GenericStyledArea.java | 18 +++++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java b/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java index 24552256..ee6fb0f7 100644 --- a/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java +++ b/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsDemo.java @@ -111,9 +111,7 @@ public void start(Stage primaryStage) { // run: `cleanupWhenNoLongerNeedIt.unsubscribe();` */ // recompute syntax highlighting only for visible paragraph changes - // Note that this shows how it can be done but is not recommended for production where multi- - // line syntax requirements are needed, like comment blocks without a leading * on each line. - codeArea.setVisibleOnlyStyler( p -> p.restyle( 0, computeHighlighting( p.getText() ) ) ); + codeArea.setVisibleOnlyStyler( (pNo,p) -> p.restyle(0, computeHighlighting(pNo, p.getText())) ); // auto-indent: insert previous line's indents on enter final Pattern whiteSpace = Pattern.compile( "^\\s+" ); @@ -137,11 +135,10 @@ public void start(Stage primaryStage) { primaryStage.show(); } - private StyleSpans> computeHighlighting(String text) { + private StyleSpans> computeHighlighting(Integer paragraphNo, String text) { Matcher matcher = PATTERN.matcher(text); int lastKwEnd = 0; - StyleSpansBuilder> spansBuilder - = new StyleSpansBuilder<>(); + StyleSpansBuilder> spansBuilder = new StyleSpansBuilder<>(); while(matcher.find()) { String styleClass = matcher.group("KEYWORD") != null ? "keyword" : diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java index a83f3fc9..3f028e75 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java @@ -778,9 +778,9 @@ public GenericStyledArea( // Initialize content virtualFlow = VirtualFlow.createVertical( getParagraphs(), - par -> { + (index, par) -> { Cell, ParagraphBox> cell = createCell( - par, + index, par, applyParagraphStyle, nodeFactory); nonEmptyCells.add(cell.getNode()); @@ -1548,9 +1548,9 @@ public final PS getParagraphStyleForInsertionAt(int pos) { } } - private UnaryOperator> visibleOnlyStyler; + private BiFunction,Paragraph> visibleOnlyStyler; /** - * This styler will only be applied to Paragraphs, just before being displayed. + * This styler will only be applied to visible Paragraphs, just before being displayed. *

Important Notes

*
    *
  1. The result of this styling does NOT modify the document model. @@ -1558,10 +1558,14 @@ public final PS getParagraphStyleForInsertionAt(int pos) { *
  2. The styler should return the result of one of Paragraph's restyle methods. *
*/ - public void setVisibleOnlyStyler(UnaryOperator> styler) { + public void setVisibleOnlyStyler(BiFunction,Paragraph> styler) { visibleOnlyStyler = styler; } + public void refreshParagraphs(int paragraphStart, int paragraphEnd) { + virtualFlow.refreshCells(paragraphStart, paragraphEnd); + } + @Override public void replaceText(int start, int end, String text) { StyledDocument doc = ReadOnlyStyledDocument.fromString( @@ -1924,11 +1928,11 @@ ParagraphBox.CaretOffsetX getTargetCaretOffset() { * ********************************************************************** */ private Cell, ParagraphBox> createCell( - Paragraph paragraph, + Integer index, Paragraph paragraph, BiConsumer applyParagraphStyle, Function, Node> nodeFactory) { - if (visibleOnlyStyler != null) paragraph = visibleOnlyStyler.apply(paragraph); + if (visibleOnlyStyler != null) paragraph = visibleOnlyStyler.apply(index, paragraph); ParagraphBox box = new ParagraphBox<>(paragraph, applyParagraphStyle, nodeFactory); box.highlightTextFillProperty().bind(highlightTextFill);