mirror of
https://github.com/processing/processing4.git
synced 2026-02-15 11:25:38 +01:00
incorporate pull #2271
This commit is contained in:
@@ -38,56 +38,62 @@ import processing.core.PApplet;
|
||||
* <p/>
|
||||
* After some further digging, this code in fact appears to be a modified
|
||||
* version of Jason Pell's GPLed "Java Beautifier" class found
|
||||
* <a href="http://www.geocities.com/jasonpell/programs.html">here</a>.
|
||||
* Which is itself based on code from Van Di-Han Ho from
|
||||
* <a href="http://www.geocities.com/~starkville/vancbj_idx.html">here</a>.
|
||||
* <a href="http://web.archive.org/web/20091018220353/http://geocities.com/jasonpell/programs.html">here</a>,
|
||||
* which is itself based on code from Van Di-Han Ho from
|
||||
* <a href="http://web.archive.org/web/20091027043013/http://www.geocities.com/~starkville/vancbj_idx.html">here</a>.
|
||||
* [Ben Fry, August 2009]
|
||||
*/
|
||||
public class AutoFormat implements Formatter {
|
||||
private char[] chars;
|
||||
private final StringBuilder buf = new StringBuilder();
|
||||
|
||||
private final StringBuilder result = new StringBuilder();
|
||||
|
||||
private int indentValue;
|
||||
private boolean EOF;
|
||||
private boolean a_flg, e_flg, if_flg, s_flag, q_flg;
|
||||
private boolean s_if_flg[];
|
||||
private boolean a_flg, if_flg, s_flag, elseFlag;
|
||||
|
||||
/** Number of ? entered without exiting at : of a?b:c structures. */
|
||||
private int conditionalLevel;
|
||||
|
||||
private int pos;
|
||||
// private int lineNumber;
|
||||
private int s_level[];
|
||||
private int c_level;
|
||||
private int sp_flg[][];
|
||||
private int s_ind[][];
|
||||
private int s_if_lev[];
|
||||
private int if_lev, level;
|
||||
private int ind[];
|
||||
private int paren;
|
||||
private int p_flg[];
|
||||
private int[][] sp_flg;
|
||||
private int[][] s_ind;
|
||||
private int if_lev;
|
||||
|
||||
/** Number of curly brackets entered and not exited. */
|
||||
private int level;
|
||||
|
||||
/** Number of parentheses entered and not exited. */
|
||||
private int parenLevel;
|
||||
|
||||
private int[] ind;
|
||||
private int[] p_flg;
|
||||
private char l_char;
|
||||
private int ct;
|
||||
private int s_tabs[][];
|
||||
private int[][] s_tabs;
|
||||
private boolean jdoc_flag;
|
||||
private char cc;
|
||||
private int tabs;
|
||||
private char c;
|
||||
private char lastNonWhitespace = 0;
|
||||
|
||||
private final Stack<Boolean> castFlags = new Stack<Boolean>();
|
||||
|
||||
|
||||
private void comment() {
|
||||
final boolean save_s_flg = s_flag;
|
||||
private void handleMultiLineComment() {
|
||||
final boolean saved_s_flag = s_flag;
|
||||
|
||||
buf.append(c = next()); // extra char
|
||||
char ch;
|
||||
buf.append(ch = nextChar()); // extra char
|
||||
while (true) {
|
||||
buf.append(c = next());
|
||||
while ((c != '/')) {
|
||||
if (c == '\n') {
|
||||
// lineNumber++;
|
||||
buf.append(ch = nextChar());
|
||||
while (ch != '/') {
|
||||
if (ch == '\n') {
|
||||
// lineNumber++;
|
||||
writeIndentedComment();
|
||||
s_flag = true;
|
||||
}
|
||||
buf.append(c = next());
|
||||
buf.append(ch = nextChar());
|
||||
}
|
||||
if (buf.length() >= 2 && buf.charAt(buf.length() - 2) == '*') {
|
||||
jdoc_flag = false;
|
||||
@@ -96,50 +102,65 @@ public class AutoFormat implements Formatter {
|
||||
}
|
||||
|
||||
writeIndentedComment();
|
||||
s_flag = save_s_flg;
|
||||
s_flag = saved_s_flag;
|
||||
jdoc_flag = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
private char get_string() {
|
||||
char ch;
|
||||
while (true) {
|
||||
buf.append(ch = next());
|
||||
if (ch == '\\') {
|
||||
buf.append(next());
|
||||
continue;
|
||||
}
|
||||
if (ch == '\'' || ch == '"') {
|
||||
buf.append(cc = next());
|
||||
while (!EOF && cc != ch) {
|
||||
if (cc == '\\') {
|
||||
buf.append(next());
|
||||
}
|
||||
buf.append(cc = next());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ch == '\n') {
|
||||
writeIndentedLine();
|
||||
a_flg = true;
|
||||
continue;
|
||||
}
|
||||
return ch;
|
||||
|
||||
private void handleSingleLineComment() {
|
||||
char ch = nextChar();
|
||||
while (ch != '\n') {
|
||||
buf.append(ch);
|
||||
ch = nextChar();
|
||||
}
|
||||
// lineNumber++;
|
||||
writeIndentedLine();
|
||||
s_flag = true;
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * Transfers buf to result until a character is reached that
|
||||
// * is neither \, \n, or in a string. \n is not written, but
|
||||
// * writeIndentedLine is called on finding it.
|
||||
// * @return The first character not listed above.
|
||||
// */
|
||||
// private char get_string() {
|
||||
// char ch1, ch2;
|
||||
// while (true) {
|
||||
// buf.append(ch1 = nextChar());
|
||||
// if (ch1 == '\\') {
|
||||
// buf.append(nextChar());
|
||||
// } else if (ch1 == '\'' || ch1 == '\"') {
|
||||
// buf.append(ch2 = nextChar());
|
||||
// while (!EOF && ch2 != ch1) {
|
||||
// if (ch2 == '\\') {
|
||||
// // Next char is escaped, ignore.
|
||||
// buf.append(nextChar());
|
||||
// }
|
||||
// buf.append(ch2 = nextChar());
|
||||
// }
|
||||
// } else if (ch1 == '\n') {
|
||||
// writeIndentedLine();
|
||||
// a_flg = true;
|
||||
// } else {
|
||||
// return ch1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
private void writeIndentedLine() {
|
||||
if (buf.length() == 0) {
|
||||
if (s_flag) {
|
||||
s_flag = a_flg = e_flg = false;
|
||||
s_flag = a_flg = elseFlag = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (s_flag) {
|
||||
final boolean shouldIndent = (tabs > 0) && (buf.charAt(0) != '{')
|
||||
&& a_flg;
|
||||
final boolean shouldIndent =
|
||||
(tabs > 0) && (buf.charAt(0) != '{') && a_flg;
|
||||
if (shouldIndent) {
|
||||
tabs++;
|
||||
}
|
||||
@@ -150,23 +171,22 @@ public class AutoFormat implements Formatter {
|
||||
}
|
||||
a_flg = false;
|
||||
}
|
||||
if (e_flg) {
|
||||
if (elseFlag) {
|
||||
if (lastNonSpaceChar() == '}') {
|
||||
trimRight(result);
|
||||
result.append(" ");
|
||||
result.append(' ');
|
||||
}
|
||||
e_flg = false;
|
||||
elseFlag = false;
|
||||
}
|
||||
result.append(buf);
|
||||
buf.setLength(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private char lastNonSpaceChar() {
|
||||
for (int i=result.length()-1; i>=0; i--) {
|
||||
for (int i = result.length() - 1; i >= 0; i--) {
|
||||
char c_i = result.charAt(i);
|
||||
if (c_i == ' ' || c_i == '\n') continue;
|
||||
else return c_i;
|
||||
if (c_i != ' ' && c_i != '\n') return c_i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -187,7 +207,7 @@ public class AutoFormat implements Formatter {
|
||||
jdoc_flag = true;
|
||||
}
|
||||
if (buf.charAt(i) == '/' && buf.charAt(i + 1) == '*') {
|
||||
if (saved_s_flag && prev() != ';') {
|
||||
if (saved_s_flag && getLastNonWhitespace() != ';') {
|
||||
result.append(buf.substring(i));
|
||||
} else {
|
||||
result.append(buf);
|
||||
@@ -204,18 +224,6 @@ public class AutoFormat implements Formatter {
|
||||
}
|
||||
|
||||
|
||||
private void handleSingleLineComment() {
|
||||
c = next();
|
||||
while (c != '\n') {
|
||||
buf.append(c);
|
||||
c = next();
|
||||
}
|
||||
// lineNumber++;
|
||||
writeIndentedLine();
|
||||
s_flag = true;
|
||||
}
|
||||
|
||||
|
||||
private void printIndentation() {
|
||||
if (tabs < 0) {
|
||||
tabs = 0;
|
||||
@@ -225,7 +233,7 @@ public class AutoFormat implements Formatter {
|
||||
}
|
||||
final int spaces = tabs * indentValue;
|
||||
for (int k = 0; k < spaces; k++) {
|
||||
result.append(" ");
|
||||
result.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,10 +246,7 @@ public class AutoFormat implements Formatter {
|
||||
}
|
||||
|
||||
|
||||
private char lastNonWhitespace = 0;
|
||||
|
||||
|
||||
private int prev() {
|
||||
private int getLastNonWhitespace() {
|
||||
return lastNonWhitespace;
|
||||
}
|
||||
|
||||
@@ -256,33 +261,32 @@ public class AutoFormat implements Formatter {
|
||||
if (pos == chars.length - 1) {
|
||||
EOF = true;
|
||||
} else {
|
||||
pos--; // reset for next()
|
||||
pos--; // reset for nextChar()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private char next() {
|
||||
private char nextChar() {
|
||||
if (EOF) {
|
||||
return 0;
|
||||
return '\0';
|
||||
}
|
||||
pos++;
|
||||
final char c;
|
||||
char retVal;
|
||||
if (pos < chars.length) {
|
||||
c = chars[pos];
|
||||
if (!Character.isWhitespace(c))
|
||||
lastNonWhitespace = c;
|
||||
retVal = chars[pos];
|
||||
if (!Character.isWhitespace(retVal))
|
||||
lastNonWhitespace = retVal;
|
||||
} else {
|
||||
c = 0;
|
||||
retVal = '\0';
|
||||
}
|
||||
if (pos == chars.length - 1) {
|
||||
if (pos == chars.length-1) {
|
||||
EOF = true;
|
||||
}
|
||||
return c;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
/* else processing */
|
||||
private void gotelse() {
|
||||
private void gotElse() {
|
||||
tabs = s_tabs[c_level][if_lev];
|
||||
p_flg[level] = sp_flg[c_level][if_lev];
|
||||
ind[level] = s_ind[c_level][if_lev];
|
||||
@@ -290,23 +294,22 @@ public class AutoFormat implements Formatter {
|
||||
}
|
||||
|
||||
|
||||
/* read to new_line */
|
||||
private boolean getnl() {
|
||||
private boolean readUntilNewLine() {
|
||||
final int savedTabs = tabs;
|
||||
char c = peek();
|
||||
while (!EOF && (c == '\t' || c == ' ')) {
|
||||
buf.append(next());
|
||||
buf.append(nextChar());
|
||||
c = peek();
|
||||
}
|
||||
|
||||
if (c == '/') {
|
||||
buf.append(next());
|
||||
buf.append(nextChar());
|
||||
c = peek();
|
||||
if (c == '*') {
|
||||
buf.append(next());
|
||||
comment();
|
||||
buf.append(nextChar());
|
||||
handleMultiLineComment();
|
||||
} else if (c == '/') {
|
||||
buf.append(next());
|
||||
buf.append(nextChar());
|
||||
handleSingleLineComment();
|
||||
return true;
|
||||
}
|
||||
@@ -315,7 +318,7 @@ public class AutoFormat implements Formatter {
|
||||
c = peek();
|
||||
if (c == '\n') {
|
||||
// eat it
|
||||
next();
|
||||
nextChar();
|
||||
// lineNumber++;
|
||||
tabs = savedTabs;
|
||||
return true;
|
||||
@@ -344,24 +347,25 @@ public class AutoFormat implements Formatter {
|
||||
|
||||
public String format(final String source) {
|
||||
final String normalizedText = source.replaceAll("\r", "");
|
||||
final String cleanText = normalizedText
|
||||
+ (normalizedText.endsWith("\n") ? "" : "\n");
|
||||
final String cleanText =
|
||||
normalizedText + (normalizedText.endsWith("\n") ? "" : "\n");
|
||||
|
||||
result.setLength(0);
|
||||
indentValue = Preferences.getInteger("editor.tabs.size");
|
||||
|
||||
// lineNumber = 0;
|
||||
q_flg = e_flg = a_flg = if_flg = false;
|
||||
// lineNumber = 0;
|
||||
boolean forFlag = a_flg = if_flg = false;
|
||||
s_flag = true;
|
||||
c_level = if_lev = level = paren = 0;
|
||||
int forParenthLevel = 0;
|
||||
conditionalLevel = parenLevel = c_level = if_lev = level = 0;
|
||||
tabs = 0;
|
||||
jdoc_flag = false;
|
||||
|
||||
s_level = new int[10];
|
||||
int[] s_level = new int[10];
|
||||
sp_flg = new int[20][10];
|
||||
s_ind = new int[20][10];
|
||||
s_if_lev = new int[10];
|
||||
s_if_flg = new boolean[10];
|
||||
int[] s_if_lev = new int[10];
|
||||
boolean[] s_if_flg = new boolean[10];
|
||||
ind = new int[10];
|
||||
p_flg = new int[10];
|
||||
s_tabs = new int[20][10];
|
||||
@@ -369,10 +373,10 @@ public class AutoFormat implements Formatter {
|
||||
chars = cleanText.toCharArray();
|
||||
// lineNumber = 1;
|
||||
|
||||
EOF = false; // set in next() when EOF
|
||||
EOF = false; // set in nextChar() when EOF
|
||||
|
||||
while (!EOF) {
|
||||
c = next();
|
||||
c = nextChar();
|
||||
switch (c) {
|
||||
default:
|
||||
buf.append(c);
|
||||
@@ -388,12 +392,13 @@ public class AutoFormat implements Formatter {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
e_flg = lookup("else");
|
||||
if (e_flg) {
|
||||
gotelse();
|
||||
elseFlag = lookup("else");
|
||||
if (elseFlag) {
|
||||
gotElse();
|
||||
if ((!s_flag) || buf.length() > 0) {
|
||||
buf.append(c);
|
||||
}
|
||||
|
||||
writeIndentedLine();
|
||||
s_flag = false;
|
||||
break;
|
||||
@@ -408,9 +413,9 @@ public class AutoFormat implements Formatter {
|
||||
if (EOF) {
|
||||
break;
|
||||
}
|
||||
e_flg = lookup("else");
|
||||
if (e_flg) {
|
||||
gotelse();
|
||||
elseFlag = lookup("else");
|
||||
if (elseFlag) {
|
||||
gotElse();
|
||||
}
|
||||
if (lookup_com("//")) {
|
||||
final char lastChar = buf.charAt(buf.length() - 1);
|
||||
@@ -422,19 +427,18 @@ public class AutoFormat implements Formatter {
|
||||
writeIndentedLine();
|
||||
result.append("\n");
|
||||
s_flag = true;
|
||||
if (e_flg) {
|
||||
if (elseFlag) {
|
||||
p_flg[level]++;
|
||||
tabs++;
|
||||
} else if (prev() == l_char) {
|
||||
} else if (getLastNonWhitespace() == l_char) {
|
||||
a_flg = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '{':
|
||||
e_flg = lookup("else");
|
||||
if (e_flg) {
|
||||
gotelse();
|
||||
}
|
||||
elseFlag = lookup("else");
|
||||
if (elseFlag) gotElse();
|
||||
|
||||
if (s_if_lev.length == c_level) {
|
||||
s_if_lev = PApplet.expand(s_if_lev);
|
||||
s_if_flg = PApplet.expand(s_if_flg);
|
||||
@@ -454,9 +458,9 @@ public class AutoFormat implements Formatter {
|
||||
buf.append(" ");
|
||||
buf.append(c);
|
||||
writeIndentedLine();
|
||||
getnl();
|
||||
readUntilNewLine();
|
||||
writeIndentedLine();
|
||||
//fprintf(outfil,"\n");
|
||||
|
||||
result.append("\n");
|
||||
tabs++;
|
||||
s_flag = true;
|
||||
@@ -489,11 +493,12 @@ public class AutoFormat implements Formatter {
|
||||
printIndentation();
|
||||
result.append(c);
|
||||
if (peek() == ';') {
|
||||
result.append(next());
|
||||
result.append(nextChar());
|
||||
}
|
||||
getnl();
|
||||
|
||||
readUntilNewLine();
|
||||
writeIndentedLine();
|
||||
result.append("\n");
|
||||
result.append('\n');
|
||||
s_flag = true;
|
||||
if (c_level < s_level[level]) {
|
||||
if (level > 0) {
|
||||
@@ -511,21 +516,21 @@ public class AutoFormat implements Formatter {
|
||||
case '"':
|
||||
case '\'':
|
||||
buf.append(c);
|
||||
cc = next();
|
||||
cc = nextChar();
|
||||
while (!EOF && cc != c) {
|
||||
buf.append(cc);
|
||||
if (cc == '\\') {
|
||||
buf.append(cc = next());
|
||||
buf.append(cc = nextChar());
|
||||
}
|
||||
if (cc == '\n') {
|
||||
// lineNumber++;
|
||||
writeIndentedLine();
|
||||
s_flag = true;
|
||||
}
|
||||
cc = next();
|
||||
cc = nextChar();
|
||||
}
|
||||
buf.append(cc);
|
||||
if (getnl()) {
|
||||
if (readUntilNewLine()) {
|
||||
l_char = cc;
|
||||
// push a newline into the stream
|
||||
chars[pos--] = '\n';
|
||||
@@ -533,13 +538,21 @@ public class AutoFormat implements Formatter {
|
||||
break;
|
||||
|
||||
case ';':
|
||||
if (forFlag) {
|
||||
// This is like a comma.
|
||||
trimRight(buf);
|
||||
buf.append("; ");
|
||||
// Not non-whitespace: allow \n.
|
||||
advanceToNonSpace();
|
||||
break;
|
||||
}
|
||||
buf.append(c);
|
||||
writeIndentedLine();
|
||||
if (p_flg[level] > 0 && ind[level] == 0) {
|
||||
tabs -= p_flg[level];
|
||||
p_flg[level] = 0;
|
||||
}
|
||||
getnl();
|
||||
readUntilNewLine();
|
||||
writeIndentedLine();
|
||||
result.append("\n");
|
||||
s_flag = true;
|
||||
@@ -555,26 +568,39 @@ public class AutoFormat implements Formatter {
|
||||
|
||||
case '\\':
|
||||
buf.append(c);
|
||||
buf.append(next());
|
||||
buf.append(nextChar());
|
||||
break;
|
||||
|
||||
case '?':
|
||||
q_flg = true;
|
||||
conditionalLevel++;
|
||||
buf.append(c);
|
||||
break;
|
||||
|
||||
case ':':
|
||||
buf.append(c);
|
||||
// Java 8 :: operator.
|
||||
if (peek() == ':') {
|
||||
writeIndentedLine();
|
||||
result.append(next());
|
||||
result.append(c).append(nextChar());
|
||||
break;
|
||||
}
|
||||
|
||||
if (q_flg) {
|
||||
q_flg = false;
|
||||
// End a?b:c structures.
|
||||
else if (conditionalLevel>0) {
|
||||
conditionalLevel--;
|
||||
buf.append(c);
|
||||
break;
|
||||
}
|
||||
|
||||
else if (forFlag) {
|
||||
trimRight(buf);
|
||||
buf.append(" : ");
|
||||
// Not to non-whitespace: allow \n.
|
||||
advanceToNonSpace();
|
||||
break;
|
||||
}
|
||||
|
||||
buf.append(c);
|
||||
|
||||
if (!lookup("default") && !lookup("case")) {
|
||||
s_flag = false;
|
||||
writeIndentedLine();
|
||||
@@ -584,26 +610,27 @@ public class AutoFormat implements Formatter {
|
||||
tabs++;
|
||||
}
|
||||
if (peek() == ';') {
|
||||
result.append(next());
|
||||
result.append(nextChar());
|
||||
}
|
||||
getnl();
|
||||
readUntilNewLine();
|
||||
writeIndentedLine();
|
||||
result.append("\n");
|
||||
result.append('\n');
|
||||
s_flag = true;
|
||||
break;
|
||||
|
||||
case '/':
|
||||
final char la = peek();
|
||||
if (la == '/') {
|
||||
buf.append(c).append(next());
|
||||
final char next = peek();
|
||||
if (next == '/') {
|
||||
// call nextChar to move on.
|
||||
buf.append(c).append(nextChar());
|
||||
handleSingleLineComment();
|
||||
result.append("\n");
|
||||
} else if (la == '*') {
|
||||
} else if (next == '*') {
|
||||
if (buf.length() > 0) {
|
||||
writeIndentedLine();
|
||||
}
|
||||
buf.append(c).append(next());
|
||||
comment();
|
||||
buf.append(c).append(nextChar());
|
||||
handleMultiLineComment();
|
||||
} else {
|
||||
buf.append(c);
|
||||
}
|
||||
@@ -613,15 +640,23 @@ public class AutoFormat implements Formatter {
|
||||
|
||||
final boolean isCast = castFlags.isEmpty() ? false : castFlags.pop();
|
||||
|
||||
paren--;
|
||||
if (paren < 0) {
|
||||
paren = 0;
|
||||
parenLevel--;
|
||||
|
||||
// If we're further back than the start of a for loop, we've
|
||||
// left it.
|
||||
if (forFlag && forParenthLevel > parenLevel) {
|
||||
forFlag = false;
|
||||
}
|
||||
|
||||
if (parenLevel < 0) {
|
||||
parenLevel = 0;
|
||||
}
|
||||
|
||||
buf.append(c);
|
||||
writeIndentedLine();
|
||||
if (getnl()) {
|
||||
if (readUntilNewLine()) {
|
||||
chars[pos--] = '\n';
|
||||
if (paren != 0) {
|
||||
if (parenLevel != 0) {
|
||||
a_flg = true;
|
||||
} else if (tabs > 0 && !isCast) {
|
||||
p_flg[level]++;
|
||||
@@ -644,42 +679,16 @@ public class AutoFormat implements Formatter {
|
||||
}
|
||||
|
||||
buf.append(c);
|
||||
paren++;
|
||||
parenLevel++;
|
||||
|
||||
// isFor says "is it the start of a for?". If it is, we set forFlag and
|
||||
// forParenthLevel. If it is not parenth_lvl was incremented above and
|
||||
// that's it.
|
||||
if (isFor) {
|
||||
// TODO(feinberg): handle new-style for loops
|
||||
c = get_string();
|
||||
while (c != ';' && c != ':') {
|
||||
c = get_string();
|
||||
if (!forFlag) {
|
||||
forParenthLevel = parenLevel;
|
||||
forFlag = true;
|
||||
}
|
||||
ct = 0;
|
||||
int for_done = 0;
|
||||
while (for_done == 0) {
|
||||
c = get_string();
|
||||
while (c != ')') {
|
||||
if (c == '(') {
|
||||
ct++;
|
||||
}
|
||||
c = get_string();
|
||||
}
|
||||
if (ct != 0) {
|
||||
ct--;
|
||||
} else {
|
||||
for_done = 1;
|
||||
}
|
||||
} // endwhile for_done
|
||||
paren--;
|
||||
if (paren < 0) {
|
||||
paren = 0;
|
||||
}
|
||||
writeIndentedLine();
|
||||
if (getnl()) {
|
||||
chars[pos--] = '\n';
|
||||
p_flg[level]++;
|
||||
tabs++;
|
||||
ind[level] = 0;
|
||||
}
|
||||
break;
|
||||
} else if (isIf) {
|
||||
writeIndentedLine();
|
||||
s_tabs[c_level][if_lev] = tabs;
|
||||
|
||||
Reference in New Issue
Block a user