Add line numbers to source code viewer
This commit is contained in:
parent
430e1d13b2
commit
0f3b2d4aac
@ -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 //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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("<html>")
|
||||
.append("<head>")
|
||||
.append("<style>").append(theme.getCss()).append("</style>")
|
||||
.append("<script>").append(new String(hljs.readAllBytes(), UTF_8)).append("</script>")
|
||||
.append("<script>" + HLJS_SCRIPT + "</script>")
|
||||
.append("</head>")
|
||||
// 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("<body style=\"background-color:%s;\">", theme.getBackground()))
|
||||
.append("<pre>")
|
||||
.append("<code class=\"language-java\">")
|
||||
.append(new String(source.readAllBytes(), UTF_8))
|
||||
.append("</code>")
|
||||
.append("</pre>")
|
||||
.append("</body>")
|
||||
.append("</html>")
|
||||
.toString()
|
||||
);
|
||||
var content = new StringBuilder()
|
||||
.append("<!DOCTYPE html>") // mandatory for line numbers plugin
|
||||
.append("<html>")
|
||||
.append("<head>")
|
||||
.append("<style>").append(theme.getCss()).append("</style>")
|
||||
.append("<style>").append(HLJS_LN_CSS).append("</style>")
|
||||
.append("<script>").append(new String(hljs.readAllBytes(), UTF_8)).append("</script>")
|
||||
.append("<script>").append(new String(hljsLineNum.readAllBytes(), UTF_8)).append("</script>")
|
||||
.append("<script>" + HLJS_SCRIPT + "</script>")
|
||||
.append("</head>")
|
||||
// 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("<body style=\"background-color:%s;\">", theme.getBackground()))
|
||||
.append("<pre>")
|
||||
.append("<code class=\"language-java\">")
|
||||
.append(new String(source.readAllBytes(), UTF_8))
|
||||
.append("</code>")
|
||||
.append("</pre>")
|
||||
.append("</body>")
|
||||
.append("</html>")
|
||||
.toString();
|
||||
|
||||
webView.getEngine().loadContent(content);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
1
sampler/src/main/resources/assets/highlightjs/highlightjs-line-numbers.min.js
vendored
Normal file
1
sampler/src/main/resources/assets/highlightjs/highlightjs-line-numbers.min.js
vendored
Normal file
@ -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<o&&(i=o,o=a,a=i,i=l,l=s,s=i);0!==n.indexOf(l);)l=l.slice(1);for(;-1===n.lastIndexOf(s);)s=s.slice(0,-1);for(var c=l,u=function(e){for(var n=e;"TABLE"!==n.nodeName;)n=n.parentNode;return n}(t),d=o+1;d<a;++d){var f=p('.{0}[{1}="{2}"]',[h,m,d]);c+="\n"+u.querySelector(f).textContent}return c+="\n"+s}function n(e){try{var n=o.querySelectorAll("code.hljs,code.nohighlight");for(var t in n)n.hasOwnProperty(t)&&(n[t].classList.contains("nohljsln")||d(n[t],e))}catch(e){r.console.error("LineNumbers error: ",e)}}function d(e,n){"object"==typeof e&&r.setTimeout(function(){e.innerHTML=f(e,n)},0)}function f(e,n){var t,r,o=(t=e,{singleLine:function(e){return!!e.singleLine&&e.singleLine}(r=(r=n)||{}),startFrom:function(e,n){var t=1;isFinite(n.startFrom)&&(t=n.startFrom);var r=function(e,n){return e.hasAttribute(n)?e.getAttribute(n):null}(e,"data-ln-start-from");return null!==r&&(t=function(e,n){if(!e)return n;var t=Number(e);return isFinite(t)?t:n}(r,1)),t}(t,r)});return function e(n){var t=n.childNodes;for(var r in t){var o;t.hasOwnProperty(r)&&(o=t[r],0<(o.textContent.trim().match(a)||[]).length&&(0<o.childNodes.length?e(o):v(o.parentNode)))}}(e),function(e,n){var t=g(e);""===t[t.length-1].trim()&&t.pop();if(1<t.length||n.singleLine){for(var r="",o=0,a=t.length;o<a;o++)r+=p('<tr><td class="{0} {1}" {3}="{5}"><div class="{2}" {3}="{5}"></div></td><td class="{0} {4}" {3}="{5}">{6}</td></tr>',[l,s,c,m,h,o+n.startFrom,0<t[o].length?t[o]:" "]);return p('<table class="{0}">{1}</table>',[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<t.length;r++){o+=p('<span class="{0}">{1}</span>\n',[n,0<t[r].length?t[r]:" "])}e.innerHTML=o.trim()}}function g(e){return 0===e.length?[]:e.split(a)}function p(e,t){return e.replace(/\{(\d+)\}/g,function(e,n){return void 0!==t[n]?t[n]:e})}r.hljs?(r.hljs.initLineNumbersOnLoad=function(e){"interactive"===o.readyState||"complete"===o.readyState?n(e):r.addEventListener("DOMContentLoaded",function(){n(e)})},r.hljs.lineNumbersBlock=d,r.hljs.lineNumbersValue=function(e,n){if("string"!=typeof e)return;var t=document.createElement("code");return t.innerHTML=e,f(t,n)},(e=o.createElement("style")).type="text/css",e.innerHTML=p(".{0}{border-collapse:collapse}.{0} td{padding:0}.{1}:before{content:attr({2})}",[i,c,m]),o.getElementsByTagName("head")[0].appendChild(e)):r.console.error("highlight.js not detected!"),document.addEventListener("copy",function(e){var n,t=window.getSelection();!function(e){for(var n=e;n;){if(n.className&&-1!==n.className.indexOf("hljs-ln-code"))return 1;n=n.parentNode}}(t.anchorNode)||(n=-1!==window.navigator.userAgent.indexOf("Edge")?u(t):t.toString(),e.clipboardData.setData("text/plain",n),e.preventDefault())})}(window,document);
|
Loading…
Reference in New Issue
Block a user