Skip to content

Commit

Permalink
Provide completion inside closure based on args it isused with furthe…
Browse files Browse the repository at this point in the history
…r. No also in a var, not just inline
  • Loading branch information
klesun committed Oct 14, 2017
1 parent 3ded2a6 commit c8f96bc
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 25 deletions.
99 changes: 91 additions & 8 deletions src/org/klesun/deep_assoc_completion/resolvers/var_res/ArgRes.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package org.klesun.deep_assoc_completion.resolvers.var_res;

import com.intellij.psi.PsiElement;
import com.jetbrains.php.lang.documentation.phpdoc.psi.impl.PhpDocCommentImpl;
import com.jetbrains.php.lang.documentation.phpdoc.psi.impl.PhpDocRefImpl;
import com.jetbrains.php.lang.documentation.phpdoc.psi.impl.tags.PhpDocDataProviderImpl;
import com.jetbrains.php.lang.psi.elements.PhpExpression;
import com.jetbrains.php.lang.psi.elements.impl.*;
import org.klesun.deep_assoc_completion.DeepType;
import org.klesun.deep_assoc_completion.helpers.IFuncCtx;
import org.klesun.deep_assoc_completion.helpers.MultiType;
import org.klesun.deep_assoc_completion.resolvers.ClosRes;
import org.klesun.deep_assoc_completion.resolvers.MethRes;
import org.klesun.deep_assoc_completion.resolvers.NsFuncRes;
import org.klesun.lang.Lang;
import org.klesun.lang.Opt;
import org.klesun.lang.Tls;
Expand All @@ -29,13 +30,83 @@ private static Opt<Integer> getArgOrder(ParameterImpl param)
.map(list -> L(list.getParameters()).indexOf(param));
}

private MultiType peekOutside(ParameterImpl param)
private static L<VariableImpl> findVarReferences(VariableImpl caretVar)
{
return opt(param.getParent())
.map(psi -> psi.getParent())
.fap(toCast(FunctionImpl.class)) // closure
.map(clos -> clos.getParent())
.map(clos -> clos.getParent())
return Tls.findParent(caretVar, GroupStatementImpl.class, a -> true)
.map(funcBody -> Tls.findChildren(
funcBody, VariableImpl.class,
subPsi -> !(subPsi instanceof FunctionImpl)
))
.def(L())
.flt(varUsage -> caretVar.getName().equals(varUsage.getName()));
}

private Opt<MultiType> getArgFromNsFuncCall(FunctionReferenceImpl call, int lambdaArgOrder)
{
PsiElement[] params = call.getParameters();
if (lambdaArgOrder == 0 && params.length > 1) {
// functions where array is passed in the second argument
if ("array_map".equals(call.getName())) {
return L(call.getParameters()).gat(1)
.fap(toCast(PhpExpression.class))
.map(arr -> trace.subCtx(L()).findExprType(arr).getEl());
}
} else if (lambdaArgOrder == 1 && params.length > 1) {
// functions where array is passed in the first argument
if ("array_filter".equals(call.getName()) ||
"array_walk".equals(call.getName()) ||
"array_walk_recursive".equals(call.getName()) ||
"usort".equals(call.getName())
) {
return L(call.getParameters()).gat(0)
.fap(toCast(PhpExpression.class))
.map(arr -> trace.subCtx(L()).findExprType(arr).getEl());
}
}
return opt(null);
}

private Opt<MultiType> getArgPassedTo(VariableImpl variable)
{
return opt(variable.getParent())
.fap(parent -> Opt.fst(list(opt(null)
, Tls.cast(ParameterListImpl.class, parent)
.fap(parl -> opt(parl.getParent())
.fap(toCast(FunctionReferenceImpl.class))
.fap(func -> getArgFromNsFuncCall(func, L(parl.getParameters()).indexOf(variable))))
)));
}

// $getAirline = function($seg){return $seg['airline'];};
// array_map($getAirline, $segments);
private Opt<MultiType> getFuncVarUsageArg(FunctionImpl clos, int order)
{
return opt(clos.getParent())
.map(expr -> expr.getParent())
.fap(toCast(AssignmentExpressionImpl.class))
.fap(ass -> opt(ass.getParent())
.fap(toCast(StatementImpl.class))
.map(state -> state.getNextPsiSibling())
.map(nextSt -> {
int startOffset = nextSt.getTextOffset();
return opt(ass.getVariable())
.fap(toCast(VariableImpl.class))
.map(variable -> findVarReferences(variable))
.def(L())
.fop(res -> opt(res.getElement()))
.flt(ref -> ref.getTextOffset() >= startOffset)
.fop(toCast(VariableImpl.class))
.fop(ref -> getArgPassedTo(ref));
})
.map(mts -> new MultiType(mts.fap(mt -> mt.types))));
}

// array_map(function($seg){return $seg['airline'];}, $segments);
private Opt<MultiType> getInlineFuncArg(FunctionImpl clos, int order)
{
return opt(clos.getParent())
.flt(expr -> order == 0) //TODO: fix, this is not arg order of lambda, but arg order _in_ lambda
.map(expr -> expr.getParent())
.fap(toCast(ParameterListImpl.class))
.map(argList -> argList.getParent())
.fap(parent -> Opt.fst(list(opt(null)
Expand All @@ -47,7 +118,19 @@ private MultiType peekOutside(ParameterImpl param)
.fap(call -> L(call.getParameters()).gat(1))
)))
.fap(toCast(PhpExpression.class))
.map(arr -> trace.subCtx(L()).findExprType(arr).getEl())
.map(arr -> trace.subCtx(L()).findExprType(arr).getEl());
}

private MultiType peekOutside(ParameterImpl param)
{
return opt(param.getParent())
.map(paramList -> paramList.getParent())
.fap(toCast(FunctionImpl.class)) // closure
.fap(clos -> getArgOrder(param)
.fap(order -> Opt.fst(list(opt(null)
, getInlineFuncArg(clos, order)
, getFuncVarUsageArg(clos, order)
))))
.def(MultiType.INVALID_PSI)
;
}
Expand Down
55 changes: 38 additions & 17 deletions tests/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,6 @@ private static function testReverseType()
]);
}

//============================
// not implemented follow
//============================

public function provideConstructorCompletion()
{
$marisa = new \TouhouNs\MarisaKirisame([
Expand All @@ -206,6 +202,44 @@ public function provideConstructorCompletion()
]);
}

public function provideFuncVarUsageBasedCompletion()
{
$list = [];

$pccRecords = [
['gds' => 'apollo', 'pcc' => '1O4K'],
['gds' => 'sabre', 'pcc' => '611F'],
['gds' => 'amadeus', 'pcc' => 'RIX123456'],
];
$getPcc = function($pccRecord) use (&$list){
// should suggest 'pcc' and 'gds' based on what
// is passed to this lambda further in array_map
$pccRecord[''];
$list[] = [$pccRecord, ['pcc' => [], 'gds' => []]];
return $pccRecord['pcc'];
};
$pccs = array_filter($pccRecords, $getPcc);

return $list;
}

private static function testListAccess()
{
$mapped = self::testBasisListAccess();
$addTaxCode = function(array $taxRecord) {
$taxRecord['taxCode'] = 'YQ';
return $taxRecord;
};

$withTaxCode = array_map($addTaxCode, $mapped);
// should suggest currency, amount, taxCode
$withTaxCode[0][''];
}

//============================
// not implemented follow
//============================

private static function testUsedKeysInAVar()
{
$params = [
Expand Down Expand Up @@ -258,19 +292,6 @@ private static function testUsedKeysPassedDeeper()
]);
}

private static function testListAccess()
{
$mapped = self::testBasisListAccess();
$addTaxCode = function(array $taxRecord) {
$taxRecord['taxCode'] = 'YQ';
return $taxRecord;
};

$withTaxCode = array_map($addTaxCode, $mapped);
// should suggest currency, amount, taxCode
$withTaxCode[0][''];
}

private static function testUndefinedKeyError()
{
$record = ['a' => 6, 'b' => 8];
Expand Down

0 comments on commit c8f96bc

Please sign in to comment.