Skip to content

Commit

Permalink
Land #638: Fix highlighting of function definitions with guard clauses
Browse files Browse the repository at this point in the history
  • Loading branch information
KronicDeth committed Mar 10, 2017
2 parents 905f548 + ac49170 commit 823d06b
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 96 deletions.
7 changes: 7 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 0 additions & 21 deletions .idea/runConfigurations/runIde__15_0_6_.xml

This file was deleted.

21 changes: 0 additions & 21 deletions .idea/runConfigurations/runIde__2016_3_1_.xml

This file was deleted.

134 changes: 92 additions & 42 deletions src/org/elixir_lang/annonator/Callable.java
Expand Up @@ -14,6 +14,8 @@
import com.intellij.util.containers.ContainerUtil;
import org.elixir_lang.ElixirSyntaxHighlighter;
import org.elixir_lang.errorreport.Logger;
import org.elixir_lang.psi.AtNonNumericOperation;
import org.elixir_lang.psi.AtUnqualifiedNoParenthesesCall;
import org.elixir_lang.psi.call.Call;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -61,59 +63,105 @@ public void visitElement(@NotNull final PsiElement element) {
*/

private void visitCall(@NotNull final Call call) {
PsiReference reference = call.getReference();
if (!(call instanceof AtNonNumericOperation ||
call instanceof AtUnqualifiedNoParenthesesCall)) {
visitNonModuleAttributeCall(call);
}
}

if (reference != null) {
Collection<PsiElement> resolvedCollection = null;
private void visitCallDefinitionClause(@NotNull final Call call) {
PsiElement head = org.elixir_lang.structure_view.element.CallDefinitionClause.head(call);

if (reference instanceof PsiPolyVariantReference) {
PsiPolyVariantReference polyVariantReference = (PsiPolyVariantReference) reference;
if (head != null) {
visitCallDefinitionHead(head, call);
}
}

ResolveResult[] resolveResults;
private void visitCallDefinitionHead(@NotNull final PsiElement head, @NotNull final Call clause) {
PsiElement stripped = org.elixir_lang.structure_view.element.CallDefinitionHead.strip(head);

try {
resolveResults = polyVariantReference.multiResolve(false);
} catch (StackOverflowError stackOverflowError) {
Logger.error(Callable.class, "StackOverflowError when annotating Call", call);
resolveResults = new ResolveResult[0];
}
if (stripped instanceof Call) {
visitStrippedCallDefinitionHead((Call) stripped, clause);
}
}

private void visitNonModuleAttributeCall(@NotNull final Call call) {
if (org.elixir_lang.structure_view.element.CallDefinitionClause.is(call)) {
visitCallDefinitionClause(call);
} else {
PsiReference reference = call.getReference();

if (reference != null) {
Collection<PsiElement> resolvedCollection = null;

if (reference instanceof PsiPolyVariantReference) {
PsiPolyVariantReference polyVariantReference = (PsiPolyVariantReference) reference;

ResolveResult[] resolveResults;

List<ResolveResult> validResolveResults = ContainerUtil.filter(
resolveResults,
new Condition<ResolveResult>() {
@Override
public boolean value(ResolveResult resolveResult) {
return resolveResult.isValidResult();
try {
resolveResults = polyVariantReference.multiResolve(false);
} catch (StackOverflowError stackOverflowError) {
Logger.error(Callable.class, "StackOverflowError when annotating Call", call);
resolveResults = new ResolveResult[0];
}

List<ResolveResult> validResolveResults = ContainerUtil.filter(
resolveResults,
new Condition<ResolveResult>() {
@Override
public boolean value(ResolveResult resolveResult) {
return resolveResult.isValidResult();
}
}
}
);
resolvedCollection = ContainerUtil.map(
validResolveResults,
new com.intellij.util.Function<ResolveResult, PsiElement>() {
@Override
public PsiElement fun(ResolveResult resolveResult) {
return resolveResult.getElement();
);
resolvedCollection = ContainerUtil.map(
validResolveResults,
new com.intellij.util.Function<ResolveResult, PsiElement>() {
@Override
public PsiElement fun(ResolveResult resolveResult) {
return resolveResult.getElement();
}
}
}
);
} else {
PsiElement resolved = reference.resolve();
);
} else {
PsiElement resolved = reference.resolve();

if (resolved != null) {
resolvedCollection = Collections.singleton(resolved);
if (resolved != null) {
resolvedCollection = Collections.singleton(resolved);
}
}
}

if (resolvedCollection != null) {
for (PsiElement resolved : resolvedCollection) {
highlight(call, reference.getRangeInElement(), resolved, holder);
if (resolvedCollection != null) {
for (PsiElement resolved : resolvedCollection) {
highlight(call, reference.getRangeInElement(), resolved, holder);
}
}
} else if (isBitStreamSegmentOption(call)) {
String name = call.getName();

if (name != null && BIT_STRING_TYPES.contains(name)) {
highlight(call, holder, ElixirSyntaxHighlighter.TYPE);
}
}
}
}

private void visitStrippedCallDefinitionHead(@NotNull final Call stripped,
@NotNull final Call clause) {
PsiElement functionNameElement = stripped.functionNameElement();

if (functionNameElement != null) {
TextAttributesKey textAttributeKey = null;

if (org.elixir_lang.structure_view.element.CallDefinitionClause.isFunction(clause)) {
textAttributeKey = ElixirSyntaxHighlighter.FUNCTION_DECLARATION;
} else if (org.elixir_lang.structure_view.element.CallDefinitionClause.isMacro(clause)) {
textAttributeKey = ElixirSyntaxHighlighter.MACRO_DECLARATION;
}
} else if (isBitStreamSegmentOption(call)) {
String name = call.getName();

if (name != null && BIT_STRING_TYPES.contains(name)) {
highlight(call, holder, ElixirSyntaxHighlighter.TYPE);
if (textAttributeKey != null) {
highlight(functionNameElement, holder, textAttributeKey);
}
}
}
Expand Down Expand Up @@ -143,12 +191,14 @@ private void highlight(@NotNull Call referrer,
switch (parameterType) {
case FUNCTION_NAME:
referrerTextAttributesKey = ElixirSyntaxHighlighter.FUNCTION_CALL;
resolvedTextAttributesKey = ElixirSyntaxHighlighter.FUNCTION_DECLARATION;
// will be handled visitCallDefinitionClause
resolvedTextAttributesKey = null;
break;

case MACRO_NAME:
referrerTextAttributesKey = ElixirSyntaxHighlighter.MACRO_CALL;
resolvedTextAttributesKey = ElixirSyntaxHighlighter.MACRO_DECLARATION;
// will be handled visitCallDefinitionClause
resolvedTextAttributesKey = null;
break;

case VARIABLE:
Expand Down
7 changes: 6 additions & 1 deletion src/org/elixir_lang/annonator/Parameter.java
Expand Up @@ -8,6 +8,7 @@
import org.elixir_lang.psi.*;
import org.elixir_lang.psi.call.Call;
import org.elixir_lang.psi.operation.InMatch;
import org.elixir_lang.psi.operation.When;
import org.elixir_lang.structure_view.element.CallDefinitionClause;
import org.elixir_lang.structure_view.element.Delegation;
import org.jetbrains.annotations.Contract;
Expand Down Expand Up @@ -128,7 +129,11 @@ private static Parameter putParameterized(@NotNull final Parameter parameter, @N
Parameter parameterizedParameter;
PsiElement parent = ancestor.getParent();

if (parent instanceof Call) {
/* MUST be before `Call` because `When` operations are `Call` implementations too in all cases even though
`When` is not a subinterface. */
if (parent instanceof When) {
parameterizedParameter = putParameterized(parameter, parent);
} else if (parent instanceof Call) {
parameterizedParameter = putParameterized(parameter, (Call) parent);
} else if (parent instanceof AtNonNumericOperation ||
parent instanceof ElixirAccessExpression ||
Expand Down
19 changes: 14 additions & 5 deletions src/org/elixir_lang/psi/impl/ElixirPsiImplUtil.java
Expand Up @@ -5176,11 +5176,20 @@ public static Integer resolvedPrimaryArity(@NotNull final Call call) {
}
}

if (isPipe(call.getParent())) {
if (primaryArity == null) {
resolvedPrimaryArity = 1;
} else {
resolvedPrimaryArity += 1;
PsiElement parent = call.getParent();

if (isPipe(parent)) {
Arrow parentPipeOperation = (Arrow) parent;
PsiElement pipedInto = parentPipeOperation.rightOperand();

/* only the right operand has its arity increased because it is the operand that has the output of the
left operand prepended to its arguments */
if (pipedInto != null && call.isEquivalentTo(pipedInto)) {
if (primaryArity == null) {
resolvedPrimaryArity = 1;
} else {
resolvedPrimaryArity += 1;
}
}
}
}
Expand Down

0 comments on commit 823d06b

Please sign in to comment.