package org.rubypeople.rdt.internal.core.builder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jruby.lexer.yacc.IDESourcePosition;
import org.jruby.lexer.yacc.ISourcePosition;
import org.jruby.lexer.yacc.SyntaxException;
import org.rubypeople.rdt.core.compiler.CategorizedProblem;
import org.rubypeople.rdt.core.compiler.IProblem;
import org.rubypeople.rdt.internal.core.parser.Error;
public class SyntaxExceptionHandler {
public static CategorizedProblem handle(SyntaxException e, String contents) {
String restOfSource = contents.substring(e.getPosition().getStartOffset());
if (restOfSource != null && e.getMessage().trim().endsWith("but found '=' instead") && restOfSource.startsWith("begin")) {
// we have a multiline comment that doesn't start at first character of line.
int endIndex = restOfSource.indexOf("=end");
if (endIndex == -1) {
endIndex = contents.length();
} else {
endIndex += e.getPosition().getStartOffset() + 3;
}
ISourcePosition pos = new IDESourcePosition(e.getPosition().getFile(), e.getPosition().getStartLine(),
e.getPosition().getEndLine(), e.getPosition().getStartOffset() - 1, endIndex);
CategorizedProblem problem = new Error(pos, "Multine Comment must start at beginning of line", IProblem.MultineCommentNotAtFirstColumn);
// TODO Add arguments into problem?
return problem;
} else if (e.getMessage().trim().endsWith("unexpected end-of-file")) {
// cases like "@donut."
Pattern p = Pattern.compile("[@+|$|:]?\\w+\\.[;|\\s]");
Matcher m = p.matcher(contents);
if (m.find()) {
int startLine = getLineOfOffset(m.start(), contents);
int endLine = getLineOfOffset(m.end(), contents);
ISourcePosition pos = new IDESourcePosition(e.getPosition().getFile(), startLine, endLine, m.start(), m.end());
return new Error(pos, "Method invocation without method name", IProblem.Syntax);
}
}
return grabPrecedingPrefixForPosition(e, contents);
}
private static CategorizedProblem grabPrecedingPrefixForPosition(SyntaxException e, String contents) {
int offset = e.getPosition().getStartOffset();
String prefix = getLeadingPrefix(contents, offset);
ISourcePosition pos = new IDESourcePosition(e.getPosition().getFile(), e.getPosition().getStartLine(), e.getPosition().getEndLine(), offset - prefix.length() - 1, offset - 1);
return new Error(pos, e.getMessage(), IProblem.Syntax);
}
private static int getLineOfOffset(int offset, String contents) {
String[] lines = contents.split("\\r|\\n|\\r\\n");
final int lineDelimeterLength = getLineDelimeterLength(contents);
int start = 0;
for (int i = 0; i < lines.length; i++) {
int end = start + lines[i].length();
if (offset <= end) return i + 1; // line numbers are 1 based, while array is 0 based
start = end + lineDelimeterLength;
}
return 1;
}
private static int getLineDelimeterLength(String string) {
int index = string.indexOf('\n');
if (index == -1) return 1; // \r
if (index == 0) return 1; // \n as first char
char c = string.charAt(index - 1);
if (c == '\r') return 2; // \r\n
return 1; // \n
}
private static String getLeadingPrefix(String contents, int offset) {
StringBuffer buffer = new StringBuffer();
for (int i = offset - 1; i >= 0; i--) {
char c = contents.charAt(i);
if (Character.isWhitespace(c)) break;
buffer.insert(0, c);
}
return buffer.toString();
}
}