package org.jetbrains.plugins.ruby.motion;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.template.TemplateBuilder;
import com.intellij.codeInsight.template.TemplateBuilderFactory;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ruby.motion.bridgesupport.Function;
import org.jetbrains.plugins.ruby.motion.symbols.FunctionSymbol;
import org.jetbrains.plugins.ruby.ruby.codeInsight.completion.MethodInsertHandlerCreator;
import org.jetbrains.plugins.ruby.ruby.codeInsight.completion.RubyAbstractMethodInsertHandler;
import org.jetbrains.plugins.ruby.ruby.codeInsight.completion.RubyCompletionProvider;
import org.jetbrains.plugins.ruby.ruby.codeInsight.completion.RubyLookupElement;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.fqn.FQN;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.Symbol;
import org.jetbrains.plugins.ruby.ruby.codeInsight.types.RType;
import org.jetbrains.plugins.ruby.ruby.lang.psi.RPsiElement;
import org.jetbrains.plugins.ruby.ruby.lang.psi.methodCall.RCall;
import java.util.ArrayList;
import java.util.List;
/**
* @author Dennis.Ushakov
*/
public class RubyMotionCompletionProvider extends RubyCompletionProvider {
@Nullable
@Override
public LookupElement createLookupItem(@NotNull Symbol symbol,
String name,
boolean bold,
@Nullable Symbol typeSourceSymbol,
@Nullable RType originalType,
boolean isInsertHandlerCanBeApplied,
@Nullable MethodInsertHandlerCreator insertHandler,
@Nullable FQN symbolFQN) {
if (symbol instanceof FunctionSymbol) {
return createLookupItem((FunctionSymbol)symbol, bold, isInsertHandlerCanBeApplied);
}
return null;
}
private static LookupElement createLookupItem(@NotNull final FunctionSymbol symbol,
final boolean bold,
final boolean isInsertHandlerCanBeApplied) {
final Symbol parent = symbol.getParentSymbol();
if (parent == null || !isInsertHandlerCanBeApplied) return null;
final Function function = symbol.getFunction();
if (function.getArguments().size() < 2 || !function.getName().contains(":")) return null;
return new RubyLookupElement(function.getName(), "", parent.getFQNWithNesting().getFullPath(), bold, AllIcons.Nodes.Method,
null, new SelectorInsertHandler(function));
}
private static class SelectorInsertHandler extends RubyAbstractMethodInsertHandler {
private final Function myFunction;
public SelectorInsertHandler(Function function) {
super(null);
myFunction = function;
}
@Override
public void handleInsertMethodSignature(final InsertionContext context, final RPsiElement method, final String lookupItem) {
final String[] split = myFunction.getName().split(":");
final Document document = context.getDocument();
final int initialOffset = context.getStartOffset();
int offset = initialOffset;
document.deleteString(offset, context.getTailOffset());
document.insertString(offset, split[0]);
offset += split[0].length();
final boolean insertParentheses = CodeStyleSettingsManager.getSettings(context.getProject()).PARENTHESES_AROUND_METHOD_ARGUMENTS;
final List<TextRange> argRanges = new ArrayList<>(split.length);
document.insertString(offset, insertParentheses ? "(" : " ");
offset++;
int offsetBefore = offset;
offset += insertArg(document, offset, 0);
argRanges.add(new TextRange(offsetBefore, offset));
for (int i = 1; i < split.length; i++) {
document.insertString(offset, ", ");
offset += 2;
final String argName = split[i];
document.insertString(offset, argName + ": ");
offset += argName.length() + 2;
offsetBefore = offset;
offset += insertArg(document, offset, i);
argRanges.add(new TextRange(offsetBefore, offset));
}
document.insertString(offset, insertParentheses ? ")" : "");
PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
if (ApplicationManager.getApplication().isUnitTestMode()) return;
final PsiElement element = context.getFile().findElementAt(initialOffset);
final RCall call = PsiTreeUtil.getParentOfType(element, RCall.class);
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(call);
for (int i = 0; i < argRanges.size(); i++) {
TextRange range = argRanges.get(i);
builder.replaceRange(range.shiftRight(-call.getTextOffset()), myFunction.getArguments().get(i).first);
}
builder.run(context.getEditor(), false);
}
private int insertArg(Document document, int offset, int i) {
final String arg = myFunction.getArguments().get(i).first;
document.insertString(offset, arg);
return arg.length();
}
}
}