package net.cakenet.jsaton.ui.tools.script.ruby;
import net.cakenet.jsaton.script.ruby.RubyScript;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.folding.Fold;
import org.fife.ui.rsyntaxtextarea.folding.FoldParser;
import org.fife.ui.rsyntaxtextarea.folding.FoldType;
import org.jruby.ast.Node;
import org.jruby.lexer.yacc.ISourcePosition;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class RubyFoldParser implements FoldParser {
private Map<RSyntaxTextArea, List<Fold>> foldCache = new HashMap<>();
public List getFolds(RSyntaxTextArea textArea) {
if (!(textArea instanceof RubyScriptEditorPane))
return null;
RubyScriptEditorPane rsp = (RubyScriptEditorPane) textArea;
RubyScript script = (RubyScript) rsp.script;
script.setScript(rsp.getText());
Node root = script.getAST();
if(root == null) {
if (foldCache.containsKey(textArea))
return foldCache.get(textArea);
return null;
}
//debug(root, " ");
List<Fold> folds = new LinkedList<>();
getFolds(folds, rsp, root, null);
foldCache.put(textArea, folds);
return folds;
}
private void getFolds(List<Fold> folds, RSyntaxTextArea area, Node node, Fold parent) {
try {
Fold passItOn = parent;
int line = node.getPosition().getLine();
if (line == -1)
return; // nilnode...
int offs = area.getLineStartOffset(line);
// update parents end position...
if (parent != null) {
int pend = parent.getEndOffset();
if (pend == Integer.MAX_VALUE || offs >= pend)
parent.setEndOffset(offs);
}
// Create new if needed...
if (isFoldable(node)) {
if (parent == null) {
passItOn = new Fold(FoldType.CODE, area, offs);
folds.add(passItOn);
} else
passItOn = parent.createChild(FoldType.CODE, offs);
}
// Get children folds...
for (Node child : node.childNodes()) {
getFolds(folds, area, child, passItOn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void debug(Node node, String indent) {
ISourcePosition pos = node.getPosition();
int line = pos.getStartLine();
System.out.println(String.format("%03d", line) + indent + node.getNodeType() + "(" + pos.getClass().getCanonicalName() + ")");
for (Node child : node.childNodes())
debug(child, indent + " ");
}
private static boolean isFoldable(Node node) {
switch (node.getNodeType()) {
case WHILENODE:
case IFNODE:
case FORNODE:
case CASENODE:
case DEFNNODE:
case DEFSNODE:
case CLASSNODE:
case SCLASSNODE:
case RESCUEBODYNODE:
case MODULENODE:
return true;
}
return false;
}
}