diff --git a/src/main/java/me/coley/c2h/Code2Html.java b/src/main/java/me/coley/c2h/Code2Html.java index c8a0b06..e3f90c8 100644 --- a/src/main/java/me/coley/c2h/Code2Html.java +++ b/src/main/java/me/coley/c2h/Code2Html.java @@ -15,11 +15,11 @@ import javafx.stage.FileChooser; import javafx.stage.Stage; import javafx.util.Pair; -import jregex.Matcher; import jregex.Pattern; import me.coley.c2h.config.*; import me.coley.c2h.config.model.*; import me.coley.c2h.ui.RuleCell; +import me.coley.c2h.util.Regex; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.controlsfx.validation.ValidationSupport; @@ -45,8 +45,8 @@ */ public class Code2Html extends Application implements Callable { // Base values - private static String css = ""; - private static String js = ""; + public static String BASE_CSS = ""; + public static String BASE_JS = ""; // Command line options/args @Option(names = {"-c", "--config"}, description = "Config to with languages and themes") private File clConfig; @@ -58,8 +58,12 @@ public class Code2Html extends Application implements Callable { private boolean clClipboard; @Option(names = {"-o", "--out"}, description = "The file to output converted HTML to") private File clOutput; + @Option(names = {"-i", "--inline"}, description = "Option to make CSS inline with the HTML elements") + private boolean clInline; @CommandLine.Parameters(index = "0", description = "The file to convert to styled HTML") private File clInput; + // Other options + private boolean inline; // Boolean indicating if CLI has priority over GUI private boolean cliExecution; // Controls @@ -77,11 +81,10 @@ public class Code2Html extends Application implements Callable { // Config private ConfigHelper helper; - public static void main(String[] args) { try { - css = IOUtils.toString(Code2Html.class.getResourceAsStream("/code.css"), UTF_8); - js = IOUtils.toString(Code2Html.class.getResourceAsStream("/code.js"), UTF_8); + BASE_CSS = IOUtils.toString(Code2Html.class.getResourceAsStream("/code.css"), UTF_8); + BASE_JS = IOUtils.toString(Code2Html.class.getResourceAsStream("/code.js"), UTF_8); } catch(Exception e) { e.printStackTrace(); System.err.println("Failed to load default resources: css/js"); @@ -134,7 +137,7 @@ public Void call() { helper = new ConfigHelper(configuration, language, theme); // Input reading & parsing String text = FileUtils.readFileToString(clInput, UTF_8); - String converted = helper.convert(text); + String converted = helper.convert(text, clInline); // Output Platform.runLater(() -> { if(clClipboard) { @@ -195,7 +198,7 @@ public void start(Stage stage) { txtJS.setFont(Font.font("monospace")); txtHTML.setEditable(false); updateCSS(); - txtJS.setText(js); + txtJS.setText(BASE_JS); txtInput.textProperty().addListener((ob, o, n) -> { // Update HTML updateHTML(); @@ -210,21 +213,9 @@ public void start(Stage stage) { if(!old.equalsIgnoreCase(cur)) { // Iterate over css tags (by matching via regex) for(Rule rule : helper.getRules()) { - String r = "(\\.({TITLE}" + rule.getName() + ")\\s*\\{({BODY}[^}]*))\\}"; - Pattern pattern = new Pattern(r); - Matcher matcher = pattern.matcher(cur); - if(!matcher.find()) - return; - String body = matcher.group("BODY"); - // Parse the body of the css class and update the regex-rule style map. - r = "({key}\\S+):\\s*({value}.+)(?=;)"; - pattern = new Pattern(r); - matcher = pattern.matcher(body); - while(matcher.find()) { - String key = matcher.group("key"); - String value = matcher.group("value"); + Regex.getCssProperties(cur, "." + rule.getName()).forEach((key, value) -> { helper.getTheme().update(rule.getName(), key, value); - } + }); } } // Update HTML @@ -309,8 +300,17 @@ public void start(Stage stage) { }); mnFile.getItems().addAll(miConfigLoad); mnFile.getItems().addAll(miConfigSave); + // TODO: Swap out menu-item for checkbox + Menu mnOptions = new Menu("Options"); + CheckMenuItem miOptionInline = new CheckMenuItem("Inline HTML style"); + miOptionInline.selectedProperty().addListener((observable, oldValue, newValue) -> { + this.inline = newValue.booleanValue(); + updateHTML(); + }); + mnOptions.getItems().addAll(miOptionInline); + // inline MenuBar menuBar = new MenuBar(); - menuBar.getMenus().addAll(mnFile, mnLang, mnTheme); + menuBar.getMenus().addAll(mnFile, mnLang, mnTheme, mnOptions); // Layout SplitPane pane = new SplitPane(txtInput, tabs); SplitPane vert = new SplitPane(pane, browser); @@ -682,7 +682,7 @@ private void updateTitle() { * Update CSS text. */ private void updateCSS() { - txtCSS.setText(helper.getPatternCSS() + css); + txtCSS.setText(helper.getPatternCSS() + BASE_CSS); } /** @@ -693,12 +693,14 @@ private void updateHTML() { return; } String text = txtInput.getText().replace("\t", " "); - String html = helper.convert(text); + String html = helper.convert(text, inline); String style = txtCSS.getText(); StringBuilder sbWeb = new StringBuilder(); - sbWeb.append(""); + sbWeb.append(""); + if (!inline) { + sbWeb.append(""); + } + sbWeb.append(""); sbWeb.append(html); sbWeb.append(""); browser.getEngine().loadContent(sbWeb.toString()); diff --git a/src/main/java/me/coley/c2h/config/ConfigHelper.java b/src/main/java/me/coley/c2h/config/ConfigHelper.java index 5f33854..b92b0d9 100644 --- a/src/main/java/me/coley/c2h/config/ConfigHelper.java +++ b/src/main/java/me/coley/c2h/config/ConfigHelper.java @@ -4,9 +4,12 @@ import jregex.Pattern; import lombok.Getter; import lombok.Setter; +import me.coley.c2h.Code2Html; import me.coley.c2h.config.model.*; +import me.coley.c2h.util.Regex; import java.util.List; +import java.util.Map; import static org.apache.commons.text.StringEscapeUtils.escapeHtml4; @@ -67,14 +70,14 @@ public void addRule(Rule rule) { * @param text * Text to convert into HTML. * + * @param inline * @return HTML of the text with matched attributes of the {@link #language current language}. */ - public String convert(String text) { + public String convert(String text, boolean inline) { Matcher matcher = getPattern().matcher(text); StringBuilder sb = new StringBuilder(); int lastEnd = 0; while(matcher.find()) { - String styleClass = getClassFromGroup(matcher); int start = matcher.start(); int end = matcher.end(); // append text not matched @@ -84,17 +87,43 @@ public String convert(String text) { } // append match String matched = escapeHtml4(text.substring(start, end)); - sb.append("" + matched + ""); + if (inline) { + String styleRules = getInlineStyleFromGroup(matcher); + sb.append("" + matched + ""); + } else { + String styleClass = getClassFromGroup(matcher); + sb.append("" + matched + ""); + } lastEnd = end; } // Append ending text not matched sb.append(escapeHtml4(text.substring(lastEnd))); // Apply line formatting to each line StringBuilder fmt = new StringBuilder(); - for(String line : sb.toString().split("\n")) - fmt.append("" + line + "\n"); - // Wrap in pre tags and slap it in an HTML page - return "
" + fmt.toString() + "
"; + if(inline) { + StringBuilder sbLineStyle = new StringBuilder(); + StringBuilder sbLinePreStyle = new StringBuilder(); + Regex.getCssProperties(Code2Html.BASE_CSS, "pre .line").forEach((key, value) -> { + sbLineStyle.append(key + ":" + value + ";"); + }); + Regex.getCssProperties(Code2Html.BASE_CSS, "pre .line::before").forEach((key, value) -> { + sbLinePreStyle.append(key + ":" + value + ";"); + }); + int lineNum = 1; + for(String line : sb.toString().split("\n")) + fmt.append("" + (lineNum++) + "" + line + "\n"); + // Wrap in pre tags and slap it in an HTML page + StringBuilder sbPreStyle = new StringBuilder(); + Regex.getCssProperties(Code2Html.BASE_CSS, "pre").forEach((key, value) -> { + sbPreStyle.append(key + ":" + value + ";"); + }); + return "
" + fmt.toString() + "
"; + } else { + for(String line : sb.toString().split("\n")) + fmt.append("" + line + "\n"); + // Wrap in pre tags and slap it in an HTML page + return "
" + fmt.toString() + "
"; + } } /** @@ -149,4 +178,24 @@ public String getClassFromGroup(Matcher matcher) { return rule.getName(); return null; } + + /** + * Fetch the CSS style to be used by the matched group. + * + * @param matcher + * Matcher that has found a group. + * + * @return CSS inline style. + */ + public String getInlineStyleFromGroup(Matcher matcher) { + for(Rule rule : getRules()) + if(matcher.group(rule.getPatternGroupName()) != null) { + StringBuilder sbStyle = new StringBuilder(); + theme.getStylesForRule(rule.getName()) + .forEach(style -> + sbStyle.append(style.getKey() + ":" + style.getValue() + ";")); + return sbStyle.toString(); + } + return ""; + } } diff --git a/src/main/java/me/coley/c2h/util/Regex.java b/src/main/java/me/coley/c2h/util/Regex.java new file mode 100644 index 0000000..a494691 --- /dev/null +++ b/src/main/java/me/coley/c2h/util/Regex.java @@ -0,0 +1,43 @@ +package me.coley.c2h.util; + +import jregex.Matcher; +import jregex.Pattern; +import org.apache.commons.text.StringEscapeUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * Utility for miscellaneous regex actions. + * + * @author Matt + */ +public class Regex { + /** + * @param cssText + * Text containing CSS to match. + * @param cssRule + * Rule to extract properties from. + * + * @return Map of property names to values. + */ + public static Map getCssProperties(String cssText, String cssRule) { + Map map = new HashMap<>(); + String regex = "(({TITLE}" + cssRule + ")\\s*\\{({BODY}[^}]*))\\}"; + Pattern pattern = new Pattern(regex); + Matcher matcher = pattern.matcher(cssText); + if(!matcher.find()) + return map; + String body = matcher.group("BODY"); + // Parse the body of the css class and update the regex-rule style map. + regex = "({key}\\S+):\\s*({value}.+)(?=;)"; + pattern = new Pattern(regex); + matcher = pattern.matcher(body); + while(matcher.find()) { + String key = matcher.group("key"); + String value = matcher.group("value"); + map.put(key, value); + } + return map; + } +} diff --git a/src/main/resources/code.css b/src/main/resources/code.css index 82dd80b..d7ef33b 100644 --- a/src/main/resources/code.css +++ b/src/main/resources/code.css @@ -14,6 +14,7 @@ pre * { font-family: monospace; } /* show line numbers */ pre .line { display: inline-block; } pre .line::before { + user-select: none; counter-increment: ln; content: counter(ln); display: inline-block;