package com.aptana.ide.editor.erb;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.jruby.ast.FCallNode;
import org.jruby.ast.Node;
import org.jruby.ast.VCallNode;
import org.jruby.ast.types.INameNode;
import org.radrails.rails.core.RailsConventions;
import org.rubypeople.rdt.core.IMethod;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IRubyProject;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.core.ISourceFolder;
import org.rubypeople.rdt.core.ISourceFolderRoot;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.codeassist.ResolveContext;
import org.rubypeople.rdt.core.search.FieldReferenceMatch;
import org.rubypeople.rdt.core.search.IRubySearchConstants;
import org.rubypeople.rdt.core.search.SearchEngine;
import org.rubypeople.rdt.core.search.SearchMatch;
import org.rubypeople.rdt.core.search.SearchPattern;
import org.rubypeople.rdt.internal.codeassist.RubyCodeResolver;
import org.rubypeople.rdt.internal.core.ERBScript;
import org.rubypeople.rdt.internal.ui.RubyPlugin;
public class ERBCodeResolver extends RubyCodeResolver
{
private ResolveContext fContext;
@Override
public void select(ResolveContext context) throws RubyModelException
{
IRubyScript script = context.getScript();
if (!(script instanceof ERBScript))
return;
this.fContext = context;
super.select(context);
}
@Override
protected void resolveInstanceVar(ResolveContext context, Node selected) throws RubyModelException
{
// Handle resolving instance vars back to the declaration in matching controller/action
IType relatedController = getController();
if (relatedController == null)
return;
getControllerInstanceVariables(relatedController);
}
@Override
protected void resolveMethodCall(ResolveContext context, Node selected) throws RubyModelException
{
if ((selected instanceof FCallNode) || (selected instanceof VCallNode))
{
INameNode nameNode = (INameNode) selected;
String methodName = nameNode.getName();
List<IRubyElement> scopes = new ArrayList<IRubyElement>();
IRubyScript helper = getRelatedHelper();
if (helper != null)
{
scopes.add(helper);
}
try
{
ISourceFolderRoot root = getActionPackSourceRoot();
if (root != null)
{
ISourceFolder helperFolder = root.getSourceFolder(new String[] { "action_view", "helpers" });
List<SearchMatch> matches = search(SearchEngine
.createRubySearchScope(new IRubyElement[] { helperFolder }), IRubyElement.TYPE, "*Helper",
IRubySearchConstants.DECLARATIONS, SearchPattern.R_PATTERN_MATCH);
for (SearchMatch match : matches)
{
IType actionViewHelper = (IType) match.getElement();
scopes.add(actionViewHelper);
}
}
List<SearchMatch> matches = search(SearchEngine.createRubySearchScope(scopes
.toArray(new IRubyElement[0])), IRubyElement.METHOD, methodName,
IRubySearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH);
for (SearchMatch match : matches)
{
IMethod method = (IMethod) match.getElement();
putResolved(context, new IRubyElement[] { method });
return;
}
}
catch (CoreException e)
{
// ignore
}
}
super.resolveMethodCall(context, selected);
}
private ISourceFolderRoot getActionPackSourceRoot()
{
try
{
ISourceFolderRoot[] roots = getRubyProject().getSourceFolderRoots();
for (int i = 0; i < roots.length; i++)
{
ISourceFolderRoot root = roots[i];
IPath path = root.getPath();
if (path.segmentCount() < 2)
continue;
String gem = path.segment(path.segmentCount() - 2);
if (gem.startsWith("actionpack"))
{
return root;
}
}
}
catch (RubyModelException e)
{
ERBPlugin.getDefault().getLog().log(e.getStatus());
}
return null;
}
private IRubyProject getRubyProject()
{
return getScript().getRubyProject();
}
private IRubyScript getRelatedHelper()
{
IFile helperFile = RailsConventions.getHelperFromView(getFile());
if (helperFile == null)
return null;
return RubyCore.create(helperFile);
}
private IRubyScript getScript()
{
return fContext.getScript();
}
private IFile getFile()
{
IRubyScript script = getScript();
if (script == null)
return null;
IResource resource = script.getResource();
if (resource == null)
return null;
if (!(resource instanceof IFile))
return null;
return (IFile) resource;
}
private IType getController()
{
return RailsConventions.getControllerTypeFromViewFile(getFile());
}
private String getActionName()
{
String viewName = getViewPath().lastSegment();
if (viewName.endsWith(".rhtml"))
{
return viewName.substring(0, viewName.length() - ".rhtml".length());
}
else if (viewName.endsWith(".html.erb"))
{
return viewName.substring(0, viewName.length() - ".html.erb".length());
}
RubyPlugin
.log("uh oh, we have a view without an .rhtml or .html.erb ending! We'll just take up until first period.");
return viewName.substring(0, viewName.indexOf('.'));
}
private IPath getViewPath()
{
return getScript().getPath();
}
private IMethod getMethod(IType type, String methodName)
{
try
{
IMethod[] methods = type.getMethods();
for (int i = 0; i < methods.length; i++)
{
if (methods[i].getElementName().equals(methodName))
{
return methods[i];
}
}
}
catch (RubyModelException e)
{
RubyPlugin.log(e);
}
return null;
}
private void getControllerInstanceVariables(IType controller)
{
List<IRubyElement> resolved = new ArrayList<IRubyElement>();
if (controller == null || !controller.exists())
return;
IMethod action = getMethod(controller, getActionName());
IRubyElement scope = controller;
if (action != null)
{
scope = action;
}
try
{
List<SearchMatch> matches = search(SearchEngine.createRubySearchScope(new IRubyElement[] { scope }),
IRubyElement.INSTANCE_VAR, "*", IRubySearchConstants.DECLARATIONS, SearchPattern.R_PATTERN_MATCH);
for (SearchMatch match : matches)
{
IRubyElement element = null;
if (match instanceof FieldReferenceMatch)
{
FieldReferenceMatch fieldRef = (FieldReferenceMatch) match;
element = fieldRef.getBinding();
}
else
{
if (match != null)
element = (IRubyElement) match.getElement();
}
IRubyElement parent = null;
if (element != null)
{
parent = element.getParent();
}
if (parent != null && parent.equals(controller))
{
resolved.add(element);
}
}
}
catch (CoreException e)
{
// ignore
}
putResolved(fContext, resolved.toArray(new IRubyElement[resolved.size()]));
}
}