package krasa.formatter.plugin.processor;
import com.google.gwt.eclipse.core.editors.java.GWTPartitions;
import com.intellij.openapi.editor.Document;
import com.intellij.psi.PsiFile;
import krasa.formatter.eclipse.JSCodeFormatterFacade;
import krasa.formatter.plugin.Range;
import krasa.formatter.settings.Settings;
import krasa.formatter.utils.FileUtils;
import org.eclipse.jdt.core.formatter.IndentManipulation;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TypedPosition;
import org.eclipse.jface.text.TypedRegion;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.wst.jsdt.core.formatter.CodeFormatter;
import java.util.ArrayList;
/**
* @author Vojtech Krasa
*/
public class JSCommentsFormatterProcessor implements Processor {
private Settings settings;
private JSCodeFormatterFacade jsCodeFormatterFacade;
public JSCommentsFormatterProcessor(Settings settings) {
this.settings = settings;
jsCodeFormatterFacade = new JSCodeFormatterFacade(settings.getJSProperties());
}
@Override
public boolean process(Document documentIJ, PsiFile file, Range range) {
if (FileUtils.isJavaScript(file) && settings.isEnableJSProcessor()) {
try {
String text = documentIJ.getText();
IDocument document = new org.eclipse.jface.text.Document(text);
TextEdit formatEdit = format(document, range);
formatEdit.apply(document);
documentIJ.setText(document.get());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return true;
}
private TextEdit format(IDocument document, Range range) {
TextEdit combinedEdit = new MultiTextEdit();
ITypedRegion[] regions = computePartitioning(document, range);
// Format all JSNI blocks in the document
int i = 0;
for (ITypedRegion region : regions) {
if (region.getType().equals(GWTPartitions.JSNI_METHOD)) {
TextEdit edit = format(document, new TypedPosition(region));
if (edit != null) {
combinedEdit.addChild(edit);
}
i++;
}
}
return combinedEdit;
}
public TextEdit format(IDocument document, TypedPosition partition) {
try {
// Extract the JSNI block out of the document
int offset = partition.getOffset();
int length = partition.getLength();
// Determine the line delimiter, indent string, and tab/indent widths
String lineDelimiter = Settings.LINE_SEPARATOR;
int tabWidth = IndentManipulation.getTabWidth(settings.getJSProperties().get());
int indentWidth = IndentManipulation.getIndentWidth(settings.getJSProperties().get());
// Get indentation level of the first line of the JSNI block (this should
// be the line containing the JSNI method declaration)
int methodDeclarationOffset = getMethodDeclarationOffset(document, offset);
int jsniLine1 = document.getLineOfOffset(methodDeclarationOffset);
int methodIndentLevel = getLineIndentLevel(document, jsniLine1, tabWidth, indentWidth);
// String indentLine = defaultCodeFormatter.createIndentationString(methodIndentLevel);
// Extract the JSNI body out of the block and split it up by line
String jsniSource = document.get(offset, length);
String formattedJs;
TextEdit formatEdit = jsCodeFormatterFacade.format(CodeFormatter.K_JAVA_DOC, jsniSource, 0, jsniSource.length(),
methodIndentLevel, lineDelimiter);
org.eclipse.jface.text.Document d = new org.eclipse.jface.text.Document(jsniSource);
formatEdit.apply(d);
formattedJs = d.get();
// if (!formattedJs.startsWith(lineDelimiter)) {
// formattedJs = indentLine+formattedJs ;
// formattedJs = lineDelimiter + formattedJs;
// }
//
if (!formattedJs.endsWith(lineDelimiter)) {
formattedJs = formattedJs + lineDelimiter;
}
// formattedJs = formattedJs + indentLine;
return new ReplaceEdit(offset, length, formattedJs);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static int getLineIndentLevel(IDocument document, int line, int tabWidth, int indentWidth)
throws BadLocationException {
int lineOffset = document.getLineOffset(line);
return getLineIndentLevel(document.get(lineOffset, document.getLineLength(line)), tabWidth, indentWidth);
}
private static int getLineIndentLevel(String line, int tabWidth, int indentWidth) {
return IndentManipulation.measureIndentUnits(line, tabWidth, indentWidth);
}
private static int getMethodDeclarationOffset(IDocument document, int offset) {
int methodDeclarationOffset = offset;
// jsniMethodWithDeclarationLineThatWraps
String s = document.get();
int i = s.lastIndexOf("\n", offset);
String substring = s.substring(i + 1, offset);
substring = substring.trim();
if (!substring.isEmpty() && !substring.contains("(")) {
methodDeclarationOffset = i - 2;
}
return methodDeclarationOffset;
}
private static ITypedRegion[] computePartitioning(IDocument document, Range range) {
ArrayList<ITypedRegion> iTypedRegions = new ArrayList<ITypedRegion>();
String str = document.get();
String prefix = "/**";
String postfix = "*/";
int startIndex = 0;
int endIndex = 0;
while (startIndex != -1) {
startIndex = str.indexOf(prefix, startIndex);
endIndex = str.indexOf(postfix, startIndex);
if (startIndex != -1 && endIndex != -1) {
endIndex = endIndex + 3;
if (isInRange(range, startIndex, endIndex)) {
iTypedRegions.add(new TypedRegion(startIndex, endIndex - startIndex, GWTPartitions.JSNI_METHOD));
}
startIndex += prefix.length();
}
}
return iTypedRegions.toArray(new ITypedRegion[iTypedRegions.size()]);
}
private static boolean isInRange(Range range, int startIndex, int endIndex) {
boolean b = range.getStartOffset() < startIndex && range.getEndOffset() > endIndex;
boolean b1 = startIndex < range.getEndOffset() && range.getEndOffset() < endIndex;
boolean b2 = startIndex < range.getStartOffset() && range.getStartOffset() < endIndex;
return range.isWholeFile() || b || b1 || b2;
}
}