Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Markdown escape all #1754

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions src/main/java/net/dv8tion/jda/api/utils/MarkdownSanitizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,95 @@ public static String escape(@Nonnull String sequence, int ignored)
.compute(sequence);
}

/**
* Escapes every single markdown formatting token found in the provided string.
* <br>Example: {@code escape("**Hello _World_", true)}
*
* @param sequence
* The string to sanitize
* @param single
* Whether it should scape single tokens or not.
*
* @throws java.lang.IllegalArgumentException
* If provided with null sequence
*
* @return The string with escaped markdown
*/
@Nonnull
public static String escape(@Nonnull String sequence, boolean single)
{
Checks.notNull(sequence, "Input");
if(!single) return escape(sequence);

StringBuilder builder = new StringBuilder();
boolean escaped = false;
boolean newline = true;
for (int i = 0; i < sequence.length(); i++)
{
char current = sequence.charAt(i);
if (newline)
{
newline = Character.isWhitespace(current); // might still be a quote if prefixed by whitespace
if (current == '>')
{
// Check for quote if line starts with angle bracket
if (i + 1 < sequence.length() && Character.isWhitespace(sequence.charAt(i+1)))
{
builder.append("\\>"); // simple quote
}
else if (i + 3 < sequence.length() && sequence.startsWith(">>>", i) && Character.isWhitespace(sequence.charAt(i+3)))
{
builder.append("\\>\\>\\>").append(sequence.charAt(i+3)); // block quote
i += 3; // since we include 3 angle brackets AND whitespace
}
else
{
builder.append(current); // just a normal angle bracket
}
continue;
}
}

if (escaped)
{
builder.append(current);
escaped = false;
continue;
}
// Handle average case
switch (current)
{
case '*': // simple markdown escapes for single characters
case '_':
case '`':
builder.append('\\').append(current);
break;
case '|': // cases that require at least 2 characters in sequence
case '~':
if (i + 1 < sequence.length() && sequence.charAt(i+1) == current)
{
builder.append('\\').append(current)
.append('\\').append(current);
i++;
}
else
builder.append(current);
break;
case '\\': // escape character
builder.append(current);
escaped = true;
break;
case '\n': // linefeed is a special case for quotes
builder.append(current);
newline = true;
break;
default:
builder.append(current);
}
}
return builder.toString();
}

/**
* Switches the used {@link net.dv8tion.jda.api.utils.MarkdownSanitizer.SanitizationStrategy}.
*
Expand Down
70 changes: 70 additions & 0 deletions src/test/java/MarkdownTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,73 @@ public void testQuote()
Assertions.assertEquals("\\> \\_Hello \n\\> World\\_", markdown.compute("> _Hello \n> World_"));
}
}

class EscapeMarkdownAllTest
{
@Test
public void testAsterisk()
{
Assertions.assertEquals("Hello\\*World", MarkdownSanitizer.escape("Hello*World", true));
Assertions.assertEquals("Hello\\*\\*World", MarkdownSanitizer.escape("Hello**World", true));
Assertions.assertEquals("Hello\\*\\*\\*World", MarkdownSanitizer.escape("Hello***World", true));

MinnDevelopment marked this conversation as resolved.
Show resolved Hide resolved
Assertions.assertEquals("Hello\\*World", MarkdownSanitizer.escape("Hello\\*World", true));
Assertions.assertEquals("Hello\\*\\*World", MarkdownSanitizer.escape("Hello\\*\\*World", true));
Assertions.assertEquals("Hello\\*\\*\\*World", MarkdownSanitizer.escape("Hello\\*\\*\\*World", true));
}

@Test
public void testUnderscore()
{
Assertions.assertEquals("Hello\\_World", MarkdownSanitizer.escape("Hello_World", true));
Assertions.assertEquals("Hello\\_\\_World", MarkdownSanitizer.escape("Hello__World", true));
Assertions.assertEquals("Hello\\_\\_\\_World", MarkdownSanitizer.escape("Hello___World", true));

Assertions.assertEquals("Hello\\_World", MarkdownSanitizer.escape("Hello\\_World", true));
Assertions.assertEquals("Hello\\_\\_World", MarkdownSanitizer.escape("Hello\\_\\_World", true));
Assertions.assertEquals("Hello\\_\\_\\_World", MarkdownSanitizer.escape("Hello\\_\\_\\_World", true));
}

@Test
public void testCodeBlock()
{
Assertions.assertEquals("Hello\\`World", MarkdownSanitizer.escape("Hello`World", true));
Assertions.assertEquals("Hello\\`\\`World", MarkdownSanitizer.escape("Hello``World", true));
Assertions.assertEquals("Hello\\`\\`\\`World", MarkdownSanitizer.escape("Hello```World", true));

Assertions.assertEquals("Hello\\`World", MarkdownSanitizer.escape("Hello\\`World", true));
Assertions.assertEquals("Hello\\`\\`World", MarkdownSanitizer.escape("Hello\\`\\`World", true));
Assertions.assertEquals("Hello\\`\\`\\`World", MarkdownSanitizer.escape("Hello\\`\\`\\`World", true));
}

@Test
public void testSpoiler()
{
Assertions.assertEquals("Hello\\|\\|World", MarkdownSanitizer.escape("Hello||World", true));
Assertions.assertEquals("Hello|World", MarkdownSanitizer.escape("Hello|World", true));

Assertions.assertEquals("Hello\\|\\|World", MarkdownSanitizer.escape("Hello\\|\\|World", true));
Assertions.assertEquals("Hello\\|World", MarkdownSanitizer.escape("Hello\\|World", true));
}

@Test
public void testStrike()
{
Assertions.assertEquals("Hello\\~\\~World", MarkdownSanitizer.escape("Hello~~World", true));
Assertions.assertEquals("Hello\\~\\~World", MarkdownSanitizer.escape("Hello\\~\\~World", true));
}

@Test
public void testQuote()
{
Assertions.assertEquals("\\> Hello World", MarkdownSanitizer.escape("> Hello World", true));
Assertions.assertEquals(">Hello World", MarkdownSanitizer.escape(">Hello World", true));
Assertions.assertEquals("\\>\\>\\> Hello World", MarkdownSanitizer.escape(">>> Hello World", true));
Assertions.assertEquals(">>>Hello World", MarkdownSanitizer.escape(">>>Hello World", true));
Assertions.assertEquals("\\>\\>\\> Hello > World\n\\> Hello >>> World\n<@12345> > Hello\n \\> Hello world", MarkdownSanitizer.escape(">>> Hello > World\n> Hello >>> World\n<@12345> > Hello\n > Hello world", true));

Assertions.assertEquals("\\> Hello World", MarkdownSanitizer.escape("\\> Hello World", true));
Assertions.assertEquals("\\>\\>\\> Hello World", MarkdownSanitizer.escape("\\>\\>\\> Hello World", true));
Assertions.assertEquals("Hello > World", MarkdownSanitizer.escape("Hello > World"));
}
}