some fixes for markdown
This commit is contained in:
parent
de1ae57da7
commit
4ca854f6c2
@ -831,6 +831,9 @@ public class FileEditor extends JFrame {
|
||||
styles.addRule("blockquote { margin: 0 0 10px 12px; padding-left: 10px; border-left: 3px solid #808080; }");
|
||||
styles.addRule("pre { font-family: Monospaced; font-size: " + fontSize + "pt; margin: 0 0 10px 0; padding: 8px; }");
|
||||
styles.addRule("code { font-family: Monospaced; }");
|
||||
styles.addRule("table { border-collapse: collapse; margin: 0 0 10px 0; }");
|
||||
styles.addRule("th, td { border: 1px solid #808080; padding: 4px 8px; }");
|
||||
styles.addRule("th { font-weight: bold; }");
|
||||
styles.addRule("a { color: #2f7ed8; }");
|
||||
return kit;
|
||||
}
|
||||
@ -838,15 +841,17 @@ public class FileEditor extends JFrame {
|
||||
private String markdownToHtml(String markdown) {
|
||||
StringBuilder body = new StringBuilder();
|
||||
String[] lines = markdown.replace("\r\n", "\n").replace('\r', '\n').split("\n", -1);
|
||||
StringBuilder paragraph = new StringBuilder();
|
||||
java.util.List<String> paragraph = new java.util.ArrayList<>();
|
||||
StringBuilder list = null;
|
||||
String listTag = null;
|
||||
boolean inFence = false;
|
||||
StringBuilder fence = new StringBuilder();
|
||||
|
||||
for (String line : lines) {
|
||||
for (int lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
||||
String line = lines[lineIndex];
|
||||
String trimmed = line.trim();
|
||||
if (trimmed.startsWith("```")) {
|
||||
String fenceLine = normalizeFenceMarker(trimmed);
|
||||
if (fenceLine.startsWith("```")) {
|
||||
if (inFence) {
|
||||
body.append("<pre><code>").append(escapeHtml(fence.toString())).append("</code></pre>");
|
||||
fence.setLength(0);
|
||||
@ -855,8 +860,15 @@ public class FileEditor extends JFrame {
|
||||
flushParagraph(body, paragraph);
|
||||
list = flushList(body, list, listTag);
|
||||
listTag = null;
|
||||
String openerRest = fenceLine.substring(3).trim();
|
||||
int sameLineClose = findFenceClose(openerRest);
|
||||
if (sameLineClose >= 0) {
|
||||
String code = stripFenceLanguage(openerRest.substring(0, sameLineClose).trim());
|
||||
body.append("<pre><code>").append(escapeHtml(code)).append("</code></pre>");
|
||||
} else {
|
||||
inFence = true;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (inFence) {
|
||||
@ -888,6 +900,14 @@ public class FileEditor extends JFrame {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isTableStart(lines, lineIndex)) {
|
||||
flushParagraph(body, paragraph);
|
||||
list = flushList(body, list, listTag);
|
||||
listTag = null;
|
||||
lineIndex = appendMarkdownTable(body, lines, lineIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
Matcher unordered = Pattern.compile("^[-*+]\\s+(.+)$").matcher(trimmed);
|
||||
Matcher ordered = Pattern.compile("^\\d+[.)]\\s+(.+)$").matcher(trimmed);
|
||||
if (unordered.matches() || ordered.matches()) {
|
||||
@ -914,10 +934,11 @@ public class FileEditor extends JFrame {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (paragraph.length() > 0) {
|
||||
paragraph.append(' ');
|
||||
if (list != null) {
|
||||
list = flushList(body, list, listTag);
|
||||
listTag = null;
|
||||
}
|
||||
paragraph.append(trimmed);
|
||||
paragraph.add(trimmed);
|
||||
}
|
||||
|
||||
if (inFence) {
|
||||
@ -928,10 +949,112 @@ public class FileEditor extends JFrame {
|
||||
return "<html><body>" + body + "</body></html>";
|
||||
}
|
||||
|
||||
private void flushParagraph(StringBuilder body, StringBuilder paragraph) {
|
||||
if (paragraph.length() == 0) return;
|
||||
body.append("<p>").append(renderInline(paragraph.toString())).append("</p>");
|
||||
paragraph.setLength(0);
|
||||
private String normalizeFenceMarker(String text) {
|
||||
return text.replace("\\`", "`");
|
||||
}
|
||||
|
||||
private int findFenceClose(String text) {
|
||||
int plain = text.indexOf("```");
|
||||
int escaped = text.indexOf("\\`\\`\\`");
|
||||
if (plain < 0) return escaped;
|
||||
if (escaped < 0) return plain;
|
||||
return Math.min(plain, escaped);
|
||||
}
|
||||
|
||||
private String stripFenceLanguage(String text) {
|
||||
int spaceIndex = text.indexOf(' ');
|
||||
if (spaceIndex <= 0) return text;
|
||||
String firstWord = text.substring(0, spaceIndex);
|
||||
if (firstWord.matches("[A-Za-z][A-Za-z0-9_+.-]*")) {
|
||||
return text.substring(spaceIndex + 1).trim();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private boolean isTableStart(String[] lines, int lineIndex) {
|
||||
if (lineIndex + 1 >= lines.length) return false;
|
||||
java.util.List<String> header = splitMarkdownTableRow(lines[lineIndex].trim());
|
||||
java.util.List<String> separator = splitMarkdownTableRow(lines[lineIndex + 1].trim());
|
||||
if (header.size() < 2 || separator.size() != header.size()) return false;
|
||||
for (String cell : separator) {
|
||||
if (!cell.trim().matches(":?-{3,}:?")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int appendMarkdownTable(StringBuilder body, String[] lines, int startIndex) {
|
||||
java.util.List<String> header = splitMarkdownTableRow(lines[startIndex].trim());
|
||||
body.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"4\"><tr>");
|
||||
for (String cell : header) {
|
||||
body.append("<th>").append(renderInline(cell.trim())).append("</th>");
|
||||
}
|
||||
body.append("</tr>");
|
||||
|
||||
int lineIndex = startIndex + 2;
|
||||
while (lineIndex < lines.length) {
|
||||
String trimmed = lines[lineIndex].trim();
|
||||
if (trimmed.isEmpty()) break;
|
||||
java.util.List<String> cells = splitMarkdownTableRow(trimmed);
|
||||
if (cells.size() != header.size()) break;
|
||||
body.append("<tr>");
|
||||
for (String cell : cells) {
|
||||
body.append("<td>").append(renderInline(cell.trim())).append("</td>");
|
||||
}
|
||||
body.append("</tr>");
|
||||
lineIndex++;
|
||||
}
|
||||
|
||||
body.append("</table>");
|
||||
return lineIndex - 1;
|
||||
}
|
||||
|
||||
private java.util.List<String> splitMarkdownTableRow(String row) {
|
||||
java.util.List<String> cells = new java.util.ArrayList<>();
|
||||
if (!row.contains("|")) return cells;
|
||||
String content = row;
|
||||
if (content.startsWith("|")) {
|
||||
content = content.substring(1);
|
||||
}
|
||||
if (content.endsWith("|")) {
|
||||
content = content.substring(0, content.length() - 1);
|
||||
}
|
||||
|
||||
StringBuilder cell = new StringBuilder();
|
||||
boolean escaped = false;
|
||||
for (int i = 0; i < content.length(); i++) {
|
||||
char c = content.charAt(i);
|
||||
if (escaped) {
|
||||
cell.append(c);
|
||||
escaped = false;
|
||||
} else if (c == '\\') {
|
||||
escaped = true;
|
||||
} else if (c == '|') {
|
||||
cells.add(cell.toString());
|
||||
cell.setLength(0);
|
||||
} else {
|
||||
cell.append(c);
|
||||
}
|
||||
}
|
||||
if (escaped) {
|
||||
cell.append('\\');
|
||||
}
|
||||
cells.add(cell.toString());
|
||||
return cells;
|
||||
}
|
||||
|
||||
private void flushParagraph(StringBuilder body, java.util.List<String> paragraph) {
|
||||
if (paragraph.isEmpty()) return;
|
||||
body.append("<p>");
|
||||
for (int i = 0; i < paragraph.size(); i++) {
|
||||
if (i > 0) {
|
||||
body.append("<br>");
|
||||
}
|
||||
body.append(renderInline(paragraph.get(i)));
|
||||
}
|
||||
body.append("</p>");
|
||||
paragraph.clear();
|
||||
}
|
||||
|
||||
private StringBuilder flushList(StringBuilder body, StringBuilder list, String listTag) {
|
||||
@ -952,25 +1075,60 @@ public class FileEditor extends JFrame {
|
||||
Matcher codeMatcher = Pattern.compile("`([^`]+)`").matcher(text);
|
||||
StringBuffer protectedText = new StringBuffer();
|
||||
while (codeMatcher.find()) {
|
||||
String token = "{{KF_MD_CODE_" + codeSpans.size() + "}}";
|
||||
String token = "%%KFCODE" + codeSpans.size() + "%%";
|
||||
codeSpans.add("<code>" + escapeHtml(codeMatcher.group(1)) + "</code>");
|
||||
codeMatcher.appendReplacement(protectedText, Matcher.quoteReplacement(token));
|
||||
}
|
||||
codeMatcher.appendTail(protectedText);
|
||||
|
||||
String html = escapeHtml(protectedText.toString());
|
||||
java.util.List<String> escapedChars = new java.util.ArrayList<>();
|
||||
String escapesProtected = protectMarkdownEscapes(protectedText.toString(), escapedChars);
|
||||
String html = escapeHtml(escapesProtected);
|
||||
html = html.replaceAll("\\*\\*([^*]+)\\*\\*", "<strong>$1</strong>");
|
||||
html = html.replaceAll("__([^_]+)__", "<strong>$1</strong>");
|
||||
html = html.replaceAll("(?<!\\*)\\*([^*]+)\\*(?!\\*)", "<em>$1</em>");
|
||||
html = html.replaceAll("(?<!_)_([^_]+)_(?!_)", "<em>$1</em>");
|
||||
html = replaceLinks(html);
|
||||
|
||||
for (int i = 0; i < escapedChars.size(); i++) {
|
||||
html = html.replace("%%KFESC" + i + "%%", escapedChars.get(i));
|
||||
}
|
||||
for (int i = 0; i < codeSpans.size(); i++) {
|
||||
html = html.replace("{{KF_MD_CODE_" + i + "}}", codeSpans.get(i));
|
||||
html = html.replace("%%KFCODE" + i + "%%", codeSpans.get(i));
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
private String protectMarkdownEscapes(String text, java.util.List<String> escapedChars) {
|
||||
StringBuilder sb = new StringBuilder(text.length());
|
||||
boolean escaped = false;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (escaped) {
|
||||
if (isMarkdownEscapable(c)) {
|
||||
String token = "%%KFESC" + escapedChars.size() + "%%";
|
||||
escapedChars.add(escapeHtml(String.valueOf(c)));
|
||||
sb.append(token);
|
||||
} else {
|
||||
sb.append('\\').append(c);
|
||||
}
|
||||
escaped = false;
|
||||
} else if (c == '\\') {
|
||||
escaped = true;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
if (escaped) {
|
||||
sb.append('\\');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private boolean isMarkdownEscapable(char c) {
|
||||
return "\\`*_{}[]<>()#+-.!|/:".indexOf(c) >= 0;
|
||||
}
|
||||
|
||||
private String replaceLinks(String html) {
|
||||
Matcher m = Pattern.compile("\\[([^\\]]+)]\\(([^\\s)]+)\\)").matcher(html);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user