Fix some markdown bugs.
This commit is contained in:
parent
0dc5aed374
commit
7483b7a8ad
1 changed files with 157 additions and 24 deletions
|
@ -60,27 +60,30 @@ public class MarkdownProcessor {
|
||||||
public MarkdownProcessor() {}
|
public MarkdownProcessor() {}
|
||||||
|
|
||||||
public MarkdownProcessor(String text) {
|
public MarkdownProcessor(String text) {
|
||||||
length = text.length();
|
init(text);
|
||||||
chars = new char[length + 2];
|
|
||||||
text.getChars(0, length, chars, 0);
|
|
||||||
chars[length] = chars[length + 1] = '\n';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MarkdownProcessor(File file) throws IOException {
|
public MarkdownProcessor(File file) throws IOException {
|
||||||
length = (int) file.length();
|
length = (int) file.length();
|
||||||
chars = new char[length + 2];
|
chars = new char[length + 2];
|
||||||
FileReader reader = new FileReader(file);
|
FileReader reader = new FileReader(file);
|
||||||
if (reader.read(chars) != length) {
|
int read = 0;
|
||||||
throw new IOException("Couldn't read file");
|
try {
|
||||||
|
while (read < length) {
|
||||||
|
int r = reader.read(chars, read, length - read);
|
||||||
|
if (r == -1)
|
||||||
|
break;
|
||||||
|
read += r;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
reader.close();
|
||||||
}
|
}
|
||||||
|
length = read;
|
||||||
chars[length] = chars[length + 1] = '\n';
|
chars[length] = chars[length + 1] = '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized String process(String text) {
|
public synchronized String process(String text) {
|
||||||
length = text.length();
|
init(text);
|
||||||
chars = new char[length + 2];
|
|
||||||
text.getChars(0, length, chars, 0);
|
|
||||||
chars[length] = chars[length + 1] = '\n';
|
|
||||||
return process();
|
return process();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +98,18 @@ public class MarkdownProcessor {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized String processLinkText(String text) {
|
||||||
|
init(text);
|
||||||
|
return processLinkText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(String text) {
|
||||||
|
length = text.length();
|
||||||
|
chars = new char[length + 2];
|
||||||
|
text.getChars(0, length, chars, 0);
|
||||||
|
chars[length] = chars[length + 1] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a link defined in the source text. If the link is not found, we call
|
* Retrieve a link defined in the source text. If the link is not found, we call
|
||||||
* lookupLink(String) to retrieve it from an external source.
|
* lookupLink(String) to retrieve it from an external source.
|
||||||
|
@ -232,7 +247,7 @@ public class MarkdownProcessor {
|
||||||
// no valid link title - escape
|
// no valid link title - escape
|
||||||
state = NONE;
|
state = NONE;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!isSpace(c) || buffer.length() > 0) {
|
||||||
buffer.append(c);
|
buffer.append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,7 +451,7 @@ public class MarkdownProcessor {
|
||||||
char lastChar = 0;
|
char lastChar = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
boolean escape = false;
|
boolean escape = false;
|
||||||
for (int k = j; k < length; k++) {
|
for (int k = j; k <= length; k++) {
|
||||||
if (chars[k] == '\n' && lastChar == '\n') {
|
if (chars[k] == '\n' && lastChar == '\n') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -449,6 +464,8 @@ public class MarkdownProcessor {
|
||||||
escape = true;
|
escape = true;
|
||||||
} else if (chars[k] == '`') {
|
} else if (chars[k] == '`') {
|
||||||
k = skipCodeSpan(k);
|
k = skipCodeSpan(k);
|
||||||
|
} else if (chars[k] == '[') {
|
||||||
|
k = skipLink(k);
|
||||||
} else if (chars[k] == c) {
|
} else if (chars[k] == c) {
|
||||||
count += 1;
|
count += 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -575,6 +592,83 @@ public class MarkdownProcessor {
|
||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int skipLink(int start) {
|
||||||
|
boolean escape = false;
|
||||||
|
int nesting = 0;
|
||||||
|
int j = start + 1;
|
||||||
|
char c;
|
||||||
|
while (j < length && (escape || chars[j] != ']' || nesting != 0)) {
|
||||||
|
c = chars[j];
|
||||||
|
if (c == '\n' && chars[j - 1] == '\n') {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (escape) {
|
||||||
|
escape = false;
|
||||||
|
} else {
|
||||||
|
escape = c == '\\';
|
||||||
|
if (!escape) {
|
||||||
|
if (c == '[') {
|
||||||
|
nesting += 1;
|
||||||
|
} else if (c == ']') {
|
||||||
|
nesting -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
int k = j;
|
||||||
|
j += 1;
|
||||||
|
boolean extraSpace = false;
|
||||||
|
if (j < length && Character.isWhitespace(chars[j])) {
|
||||||
|
j += 1;
|
||||||
|
extraSpace = true;
|
||||||
|
}
|
||||||
|
c = chars[j++];
|
||||||
|
if (c == '[') {
|
||||||
|
while (j < length && chars[j] != ']') {
|
||||||
|
if (chars[j] == '\n') {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
} else if (c == '(' && !extraSpace) {
|
||||||
|
while (j < length && chars[j] != ')' && !isSpace(chars[j])) {
|
||||||
|
if (chars[j] == '\n') {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
if (j < length && chars[j] != ')') {
|
||||||
|
while (j < length && chars[j] != ')' && Character.isWhitespace(chars[j])) {
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
if (chars[j] == '"') {
|
||||||
|
int quoteStart = j = j + 1;
|
||||||
|
int len = -1;
|
||||||
|
while (j < length && chars[j] != '\n') {
|
||||||
|
if (chars[j] == '"') {
|
||||||
|
len = j - quoteStart;
|
||||||
|
} else if (len > -1) {
|
||||||
|
if (chars[j] == ')') {
|
||||||
|
break;
|
||||||
|
} else if (!isSpace(chars[j])) {
|
||||||
|
len = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chars[j] != ')') {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
j = k;
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkLink(char c) {
|
private boolean checkLink(char c) {
|
||||||
return checkLinkInternal(c, i + 1, false);
|
return checkLinkInternal(c, i + 1, false);
|
||||||
}
|
}
|
||||||
|
@ -609,7 +703,7 @@ public class MarkdownProcessor {
|
||||||
} else if (c == ']') {
|
} else if (c == ']') {
|
||||||
nesting -= 1;
|
nesting -= 1;
|
||||||
}
|
}
|
||||||
if (c == '*' || c == '_' || c == '`') {
|
if (c == '*' || c == '_' || c == '`' || c == '[') {
|
||||||
needsEncoding = true;
|
needsEncoding = true;
|
||||||
}
|
}
|
||||||
boolean s = Character.isWhitespace(chars[j]);
|
boolean s = Character.isWhitespace(chars[j]);
|
||||||
|
@ -627,10 +721,10 @@ public class MarkdownProcessor {
|
||||||
String linkId;
|
String linkId;
|
||||||
int k = j;
|
int k = j;
|
||||||
j += 1;
|
j += 1;
|
||||||
// this is weird, bug we follow the official markup implementation here:
|
// this is weird, but we follow the official markup implementation here:
|
||||||
// only accept space between link text and link target for [][], not for []()
|
// only accept space between link text and link target for [][], not for []()
|
||||||
boolean extraSpace = false;
|
boolean extraSpace = false;
|
||||||
if (j < length && isSpace(chars[j])) {
|
if (j < length && Character.isWhitespace(chars[j])) {
|
||||||
j += 1;
|
j += 1;
|
||||||
extraSpace = true;
|
extraSpace = true;
|
||||||
}
|
}
|
||||||
|
@ -715,19 +809,13 @@ public class MarkdownProcessor {
|
||||||
}
|
}
|
||||||
buffer.append(">");
|
buffer.append(">");
|
||||||
if (needsEncoding) {
|
if (needsEncoding) {
|
||||||
b.append(escapeHtml(text)).append("</a>");
|
MarkdownProcessor wrapped = new MarkdownProcessor();
|
||||||
|
buffer.append(wrapped.processLinkText(text)).append("</a>");
|
||||||
} else {
|
} else {
|
||||||
buffer.append(escapeHtml(text)).append("</a>");
|
buffer.append(escapeHtml(text)).append("</a>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b.length() > 0) {
|
i = j + 1;
|
||||||
System.arraycopy(chars, j + 1, chars, i + b.length(), length - j - 1);
|
|
||||||
b.getChars(0, b.length(), chars, i);
|
|
||||||
length = i + (length - j - 1) + b.length();
|
|
||||||
} else {
|
|
||||||
System.arraycopy(chars, j + 1, chars, i, length - j - 1);
|
|
||||||
length -= 1 + j - i;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1021,6 +1109,51 @@ public class MarkdownProcessor {
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized String processLinkText() {
|
||||||
|
buffer = new StringBuilder((int) (length * 1.2));
|
||||||
|
line = 1;
|
||||||
|
boolean escape = false;
|
||||||
|
|
||||||
|
for (i = 0; i < length; ) {
|
||||||
|
char c = chars[i];
|
||||||
|
|
||||||
|
if (escape) {
|
||||||
|
buffer.append(c);
|
||||||
|
escape = false;
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
} else if (c == '\\') {
|
||||||
|
escape = true;
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (c) {
|
||||||
|
case '*':
|
||||||
|
case '_':
|
||||||
|
if (checkEmphasis(c)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '`':
|
||||||
|
if (checkCodeSpan(c)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '!':
|
||||||
|
if (checkImage()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.append(c);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return buffer.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
boolean isLinkQuote(char c) {
|
boolean isLinkQuote(char c) {
|
||||||
return c == '"' || c == '\'' || c == '(';
|
return c == '"' || c == '\'' || c == '(';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue