Skip to content

Commit

Permalink
[+] synchronize signature intention action
Browse files Browse the repository at this point in the history
  • Loading branch information
casteng committed Apr 16, 2020
1 parent 6deb16f commit 4935b53
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 1 deletion.
2 changes: 2 additions & 0 deletions plugin/resources/PascalBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ action.fix.statement.remove.compound=Remove compound statement
action.fix.statement.add.compound=Add compound statement
action.fix.struct.goto.super=Go to ancestor
action.fix.struct.goto.descending=Go to descending
action.fix.signature.synchronize=Synchronize routine signature
action.fix.signature.synchronize.family=Synchronize signature
ann.error.missing.routine.declaration=No declaration in interface
ann.error.missing.method.declaration=Missing method declaration
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
TTest = class
procedure Test(Arg1: Integer; NewArg: string);
end;

procedure TTest.Test(Arg1: Integer<spot>; NewArg: string</spot>);
begin
end;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
TTest = class
procedure Test(Arg1: Integer; NewArg: string);
end;

procedure TTest.Test(Arg1: Integer);
begin
end;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<html>
<body>
<p>This intention will synchronize routine or method signature with other (interface or implementation) part</p>
</body>
</html>
102 changes: 102 additions & 0 deletions plugin/src/ide/intention/SynchronizeSignature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.siberika.idea.pascal.ide.intention;

import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.siberika.idea.pascal.PascalBundle;
import com.siberika.idea.pascal.ide.actions.SectionToggle;
import com.siberika.idea.pascal.lang.psi.PasClassQualifiedIdent;
import com.siberika.idea.pascal.lang.psi.PasConstrainedTypeParam;
import com.siberika.idea.pascal.lang.psi.PasFormalParameterSection;
import com.siberika.idea.pascal.lang.psi.PasNamedIdent;
import com.siberika.idea.pascal.lang.psi.PasProcBodyBlock;
import com.siberika.idea.pascal.lang.psi.PasRoutineImplDecl;
import com.siberika.idea.pascal.lang.psi.PascalExportedRoutine;
import com.siberika.idea.pascal.lang.psi.PascalRoutine;
import com.siberika.idea.pascal.util.PsiUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;

class SynchronizeSignature extends BaseElementAtCaretIntentionAction {

@NotNull
@Override
public String getText() {
return PascalBundle.message("action.fix.signature.synchronize");
}

@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return PascalBundle.message("action.fix.signature.synchronize.family");
}

@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
PascalRoutine routine = getRoutineHeader(editor, element);
PascalRoutine target = getTargetRoutine(editor, element);
return (routine != null) && (target != null) && !getSignature(routine).equals(getSignature(target));
}

@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
PascalRoutine routine = getRoutineHeader(editor, element);
PascalRoutine target = getTargetRoutine(editor, element);
if ((null == routine) || (null == target)) {
return;
}
PasFormalParameterSection params = routine.getFormalParameterSection();
PasFormalParameterSection targetParams = target.getFormalParameterSection();
if (null == params) {
if (targetParams != null) {
targetParams.delete();
}
} else {
if (targetParams != null) {
targetParams.replace(params);
} else {
PsiElement anchor = null;
for (PsiElement child : target.getChildren()) {
if (PsiUtil.isInstanceOfAny(child, PasNamedIdent.class, PasClassQualifiedIdent.class)) {
anchor = child;
}
if (child instanceof PasConstrainedTypeParam) {
anchor = child.getNextSibling(); // closing >
}
}
if (anchor != null) {
target.addAfter(params, anchor);
}
}
}
}

private String getSignature(PascalRoutine routine) {
PasFormalParameterSection params = routine != null ? routine.getFormalParameterSection() : null;
return params != null ? params.getText() : "";
}

private PascalRoutine getTargetRoutine(Editor editor, PsiElement element) {
PascalRoutine routine = getRoutineHeader(editor, element);
PsiElement target = SectionToggle.getRoutineTarget(routine);
return target instanceof PascalRoutine ? (PascalRoutine) target : null;
}

private PascalRoutine getRoutineHeader(Editor editor, PsiElement element) {
PascalRoutine routine = PsiTreeUtil.getParentOfType(element, PascalRoutine.class);
if (routine instanceof PascalExportedRoutine) {
return routine;
} else if (routine instanceof PasRoutineImplDecl) {
PasProcBodyBlock block = ((PasRoutineImplDecl) routine).getProcBodyBlock();
Integer blockOffs = block != null ? block.getTextOffset() : null;
return (null == blockOffs) || (editor.getCaretModel().getOffset() < blockOffs) ? routine : null;
} else {
return null;
}
}

}
6 changes: 6 additions & 0 deletions plugin/src/lang/psi/PascalRoutine.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ public interface PascalRoutine extends PascalRoutineEntity, PasEntityScope {
@Nullable
PasTypeID getFunctionTypeIdent(); // TODO: remove

@NotNull
List<PasCustomAttributeDecl> getCustomAttributeDeclList();

@Nullable
PasFormalParameterSection getFormalParameterSection();

boolean isOverloaded();

boolean isOverridden();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ protected String getTestDataPath() {
public void testFromOtherUnit() {
myFixture.configureByFiles("unit1.pas", "unit2.pas");
List<PasEntityScope> decls = getDeclarations("unit1");
System.out.println("===*** decls: " + decls.size());
SearchScope scope = GlobalSearchScope.allScope(getProject());
for (PasEntityScope decl : decls) {
Document doc = myFixture.getDocument(decl.getContainingFile());
Expand Down

0 comments on commit 4935b53

Please sign in to comment.