package com.siberika.idea.pascal.ide.actions; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.SmartList; import com.siberika.idea.pascal.PascalBundle; import com.siberika.idea.pascal.PascalLanguage; import com.siberika.idea.pascal.editor.PascalRoutineActions; import com.siberika.idea.pascal.lang.psi.PasEntityScope; import com.siberika.idea.pascal.lang.psi.PasExportedRoutine; import com.siberika.idea.pascal.lang.psi.PascalNamedElement; import com.siberika.idea.pascal.lang.psi.PascalStructType; import com.siberika.idea.pascal.lang.psi.impl.PasField; import com.siberika.idea.pascal.lang.psi.impl.PascalRoutineImpl; import com.siberika.idea.pascal.ui.TreeViewStruct; import com.siberika.idea.pascal.util.DocUtil; import com.siberika.idea.pascal.util.EditorUtil; import com.siberika.idea.pascal.util.Filter; import com.siberika.idea.pascal.util.PsiUtil; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import static com.siberika.idea.pascal.PascalBundle.message; /** * Author: George Bakhtadze * Date: 26/11/2015 */ public class ActionImplement extends PascalAction { @Override public void doActionPerformed(AnActionEvent e) { PsiElement el = getElement(e); PascalRoutineImpl methodImpl = null; PasEntityScope scope = PsiTreeUtil.getParentOfType(el, PasEntityScope.class); if (scope instanceof PascalRoutineImpl) { methodImpl = (PascalRoutineImpl) scope; scope = scope.getContainingScope(); } if (!(scope instanceof PascalStructType)) { EditorUtil.showErrorHint(PascalBundle.message("action.error.notinstruct"), EditorUtil.getHintPos(getEditor(e))); return; } Collection<PasEntityScope> structs = new SmartList<PasEntityScope>(); GotoSuper.getParentStructs(structs, scope); final Set<String> existing = new HashSet<String>(); for (PasField field : scope.getAllFields()) { allowNonExistingRoutines(field, existing); } TreeViewStruct tree = new TreeViewStruct(el.getProject(), PascalBundle.message("title.override.methods", scope.getName()), structs, new Filter<PasField>() { @Override public boolean allow(PasField value) { return allowNonExistingRoutines(value, existing); } }); tree.show(); doOverride(getEditor(e), scope, el, methodImpl, tree.getSelected()); } private boolean allowNonExistingRoutines(PasField value, Set<String> existing) { if (value.fieldType == PasField.FieldType.ROUTINE) { String name = PsiUtil.getFieldName(value.getElement()); if (!existing.contains(name)) { existing.add(name); return true; } } return false; } // if methodImpl = null assuming interface part private void doOverride(final Editor editor, final PasEntityScope scope, final PsiElement el, PascalRoutineImpl methodImpl, final List<PasField> selected) { PsiElement prevMethod = getPrevMethod(el, methodImpl); final AtomicInteger offs = new AtomicInteger(); if (prevMethod != null) { offs.set(prevMethod.getTextRange().getEndOffset()); } else { PsiElement pos = PsiUtil.findEndSibling(scope.getFirstChild()); offs.set(pos != null ? pos.getTextRange().getStartOffset() : -1); } if (offs.get() < 0) { EditorUtil.showErrorHint(PascalBundle.message("action.error.find.position"), EditorUtil.getHintPos(editor)); return; } PsiFile file = el.getContainingFile(); final Document document = editor.getDocument(); for (final PasField field : selected) { new WriteCommandAction(el.getProject()) { @Override protected void run(@NotNull Result result) throws Throwable { CommandProcessor.getInstance().setCurrentCommandName(PascalBundle.message("action.override")); PascalNamedElement element = field.getElement(); if (PsiUtil.isElementUsable(element)) { CharSequence text = prepareText(element.getText()); document.insertString(offs.get(), text); offs.addAndGet(text.length()); } editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); PsiDocumentManager.getInstance(el.getProject()).commitDocument(document); } }.execute(); } DocUtil.reformat(scope, true); for (final PasField field : selected) { PascalNamedElement element = field.getElement(); if (PsiUtil.isElementUsable(element)) { PasField routine = scope.getField(PsiUtil.getFieldName(field.getElement())); PascalRoutineActions.ActionImplement act = routine != null ? new PascalRoutineActions.ActionImplement(message("action.implement"), routine.getElement()) : null; if (act != null) { act.invoke(el.getProject(), editor, file); } } } } private CharSequence prepareText(String text) { return text.replace("virtual", "override").replaceAll("abstract\\s*;", ""); } private PsiElement getPrevMethod(PsiElement el, PascalRoutineImpl methodImpl) { if (methodImpl != null) { return SectionToggle.retrieveDeclaration(methodImpl, false); } PasExportedRoutine routine = (el instanceof PasExportedRoutine) ? (PasExportedRoutine) el : PsiTreeUtil.getParentOfType(el, PasExportedRoutine.class); if (null == routine) { routine = PsiTreeUtil.getPrevSiblingOfType(el, PasExportedRoutine.class); } return routine; } @Override public void update(AnActionEvent e) { e.getPresentation().setEnabledAndVisible(PascalLanguage.INSTANCE.equals(e.getData(LangDataKeys.LANGUAGE))); } }