From 0f3b2d4aac1e3a15c29edf840e6e52979eafba6d Mon Sep 17 00:00:00 2001 From: mkpaz Date: Mon, 5 Sep 2022 11:54:01 +0400 Subject: [PATCH] Add line numbers to source code viewer --- .../atlantafx/sampler/page/AbstractPage.java | 31 ++++++++-- .../atlantafx/sampler/page/CodeViewer.java | 56 ++++++++++--------- .../sampler/theme/HighlightJSTheme.java | 4 +- .../highlightjs-line-numbers.min.js | 1 + 4 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 sampler/src/main/resources/assets/highlightjs/highlightjs-line-numbers.min.js diff --git a/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java b/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java index 34b6b04..1d396f4 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java @@ -4,6 +4,8 @@ package atlantafx.sampler.page; import atlantafx.base.controls.Popover; import atlantafx.base.controls.Spacer; import atlantafx.base.theme.Styles; +import atlantafx.sampler.event.DefaultEventBus; +import atlantafx.sampler.event.ThemeEvent; import atlantafx.sampler.layout.Overlay; import atlantafx.sampler.theme.ThemeManager; import javafx.event.ActionEvent; @@ -57,6 +59,13 @@ public abstract class AbstractPage extends BorderPane implements Page { getStyleClass().add("page"); createPageLayout(); + + // update code view color theme on app theme change + DefaultEventBus.getInstance().subscribe(ThemeEvent.class, e -> { + if (ThemeManager.getInstance().getTheme() != null && isCodeViewActive()) { + loadSourceCodeAndMoveToFront(); + } + }); } protected void createPageLayout() { @@ -156,14 +165,16 @@ public abstract class AbstractPage extends BorderPane implements Page { } protected void toggleSourceCode() { - var graphic = (FontIcon) sourceCodeToggleBtn.getGraphic(); - - if (graphic.getIconCode() == ICON_SAMPLE) { + if (isCodeViewActive()) { codeViewerWrapper.toBack(); - graphic.setIconCode(ICON_CODE); + ((FontIcon) sourceCodeToggleBtn.getGraphic()).setIconCode(ICON_CODE); return; } + loadSourceCodeAndMoveToFront(); + } + + protected void loadSourceCodeAndMoveToFront() { var sourceFileName = getClass().getSimpleName() + ".java"; try (var stream = getClass().getResourceAsStream(sourceFileName)) { Objects.requireNonNull(stream, "Missing source file '" + sourceFileName + "';"); @@ -172,13 +183,25 @@ public abstract class AbstractPage extends BorderPane implements Page { ThemeManager tm = ThemeManager.getInstance(); codeViewer.setContent(stream, tm.getMatchingSourceCodeHighlightTheme(tm.getTheme())); + var graphic = (FontIcon) sourceCodeToggleBtn.getGraphic(); graphic.setIconCode(ICON_SAMPLE); + codeViewerWrapper.toFront(); } catch (IOException e) { throw new RuntimeException(e); } } + protected final boolean isSampleViewActive() { + var graphic = (FontIcon) sourceCodeToggleBtn.getGraphic(); + return graphic.getIconCode() == ICON_CODE; + } + + protected final boolean isCodeViewActive() { + var graphic = (FontIcon) sourceCodeToggleBtn.getGraphic(); + return graphic.getIconCode() == ICON_SAMPLE; + } + /////////////////////////////////////////////////////////////////////////// // Helpers // /////////////////////////////////////////////////////////////////////////// diff --git a/sampler/src/main/java/atlantafx/sampler/page/CodeViewer.java b/sampler/src/main/java/atlantafx/sampler/page/CodeViewer.java index c9f8015..36966c5 100644 --- a/sampler/src/main/java/atlantafx/sampler/page/CodeViewer.java +++ b/sampler/src/main/java/atlantafx/sampler/page/CodeViewer.java @@ -16,7 +16,10 @@ import static java.nio.charset.StandardCharsets.UTF_8; public class CodeViewer extends AnchorPane { private static final String HLJS_LIB = "assets/highlightjs/highlight.min.js"; - private static final String HLJS_SCRIPT = "hljs.highlightAll();"; + private static final String HLJS_SCRIPT = "hljs.highlightAll();hljs.initLineNumbersOnLoad();"; + + private static final String HLJS_LN_LIB = "assets/highlightjs/highlightjs-line-numbers.min.js"; + private static final String HLJS_LN_CSS = ".hljs-ln-numbers { padding-right: 20px !important;;}"; private WebView webView; @@ -36,32 +39,33 @@ public class CodeViewer extends AnchorPane { public void setContent(InputStream source, HighlightJSTheme theme) { lazyLoadWebView(); - try (var hljs = Resources.getResourceAsStream(HLJS_LIB)) { + try (var hljs = Resources.getResourceAsStream(HLJS_LIB); + var hljsLineNum = Resources.getResourceAsStream(HLJS_LN_LIB)) { - // NOTE: - // Line numbers aren't here because Highlight JS itself doesn't support it - // and highlighjs-line-numbers plugin break both indentation and colors. - webView.getEngine().loadContent( - new StringBuilder() - .append("") - .append("") - .append("") - .append("") - .append("") - .append("") - // Transparent background is allowed starting from OpenJFX 18. - // https://bugs.openjdk.org/browse/JDK-8090547 - // Until that it should match Highlight JS background. - .append(String.format("", theme.getBackground())) - .append("
")
-                            .append("")
-                            .append(new String(source.readAllBytes(), UTF_8))
-                            .append("")
-                            .append("
") - .append("") - .append("") - .toString() - ); + var content = new StringBuilder() + .append("") // mandatory for line numbers plugin + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + // Transparent background is allowed starting from OpenJFX 18. + // https://bugs.openjdk.org/browse/JDK-8090547 + // Until that it should match Highlight JS background. + .append(String.format("", theme.getBackground())) + .append("
")
+                    .append("")
+                    .append(new String(source.readAllBytes(), UTF_8))
+                    .append("")
+                    .append("
") + .append("") + .append("") + .toString(); + + webView.getEngine().loadContent(content); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/sampler/src/main/java/atlantafx/sampler/theme/HighlightJSTheme.java b/sampler/src/main/java/atlantafx/sampler/theme/HighlightJSTheme.java index 1edeb8d..7d8283f 100644 --- a/sampler/src/main/java/atlantafx/sampler/theme/HighlightJSTheme.java +++ b/sampler/src/main/java/atlantafx/sampler/theme/HighlightJSTheme.java @@ -34,8 +34,8 @@ public class HighlightJSTheme { public static HighlightJSTheme nordLight() { // there's no Nord light theme, // below is "stackoverflow-light" theme with changed background - var css = "*/.hljs{color:#2f3337;background:#ECEFF4}.hljs-subst{color:#2f3337}.hljs-comment{color:#656e77}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag{color:#015692}.hljs-attribute{color:#803378}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type{color:#b75501}.hljs-selector-class{color:#015692}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#54790d}.hljs-meta,.hljs-selector-pseudo{color:#015692}.hljs-built_in,.hljs-literal,.hljs-title{color:#b75501}.hljs-bullet,.hljs-code{color:#535a60}.hljs-meta .hljs-string{color:#54790d}.hljs-deletion{color:#c02d2e}.hljs-addition{color:#2f6f44}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}"; - var bg = "ECEFF4"; + var css = "*/.hljs{color:#2f3337;background:#f4f5f8}.hljs-subst{color:#2f3337}.hljs-comment{color:#656e77}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag{color:#015692}.hljs-attribute{color:#803378}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type{color:#b75501}.hljs-selector-class{color:#015692}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#54790d}.hljs-meta,.hljs-selector-pseudo{color:#015692}.hljs-built_in,.hljs-literal,.hljs-title{color:#b75501}.hljs-bullet,.hljs-code{color:#535a60}.hljs-meta .hljs-string{color:#54790d}.hljs-deletion{color:#c02d2e}.hljs-addition{color:#2f6f44}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}"; + var bg = "#f4f5f8"; return new HighlightJSTheme(css, bg); } diff --git a/sampler/src/main/resources/assets/highlightjs/highlightjs-line-numbers.min.js b/sampler/src/main/resources/assets/highlightjs/highlightjs-line-numbers.min.js new file mode 100644 index 0000000..a5f9f20 --- /dev/null +++ b/sampler/src/main/resources/assets/highlightjs/highlightjs-line-numbers.min.js @@ -0,0 +1 @@ +!function(r,o){"use strict";var e,i="hljs-ln",l="hljs-ln-line",h="hljs-ln-code",s="hljs-ln-numbers",c="hljs-ln-n",m="data-line-number",a=/\r\n|\r|\n/g;function u(e){for(var n=e.toString(),t=e.anchorNode;"TD"!==t.nodeName;)t=t.parentNode;for(var r=e.focusNode;"TD"!==r.nodeName;)r=r.parentNode;var o=parseInt(t.dataset.lineNumber),a=parseInt(r.dataset.lineNumber);if(o==a)return n;var i,l=t.textContent,s=r.textContent;for(a
{6}',[l,s,c,m,h,o+n.startFrom,0{1}',[i,r])}return e}(e.innerHTML,o)}function v(e){var n=e.className;if(/hljs-/.test(n)){for(var t=g(e.innerHTML),r=0,o="";r{1}\n',[n,0