Skip to content

Commit 3c0c8f3

Browse files
committed
Infer array element type from .push()
1 parent b4fbde6 commit 3c0c8f3

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

src/org/klesun/deep_js_completion/resolvers/VarRes.scala

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package org.klesun.deep_js_completion.resolvers
22

33
import java.util
4+
import java.util.Objects
45

6+
import com.intellij.lang.javascript.dialects.JSDialectSpecificHandlersFactory
57
import com.intellij.lang.javascript.psi.impl.{JSDefinitionExpressionImpl, JSFunctionImpl, JSReferenceExpressionImpl}
68
import com.intellij.lang.javascript.psi._
9+
import com.intellij.lang.javascript.psi.resolve.JSResolveUtil
710
import com.intellij.lang.javascript.psi.types._
11+
import com.intellij.psi.impl.source.resolve.ResolveCache.PolyVariantResolver
812
import com.intellij.psi.{PsiElement, PsiFile}
913
import com.intellij.psi.impl.source.resolve.reference.impl.providers.{FileReference, FileReferenceSet}
1014
import com.intellij.psi.util.PsiTreeUtil
1115
import org.klesun.deep_js_completion.entry.PathStrGoToDecl
1216
import org.klesun.deep_js_completion.helpers.{ICtx, MultiType}
17+
import org.klesun.lang.Lang
1318

1419
import scala.collection.JavaConverters._
1520
import org.klesun.lang.Lang._
@@ -87,6 +92,17 @@ case class VarRes(ctx: ICtx) {
8792
.take(1).toList.lift(0)
8893
}
8994

95+
def findUsages(ref: JSReferenceExpression): List[JSReferenceExpression] = {
96+
Option(ref.resolve()).toList.flatMap(decl => {
97+
val scope: PsiElement = Lang.findParent[JSFunctionExpression](decl)
98+
.getOrElse(decl.getContainingFile)
99+
Lang.findChildren[JSReferenceExpression](scope)
100+
.filter(usage => Objects.equals(usage.getReferenceName, ref.getReferenceName))
101+
.filter(usage => !Objects.equals(usage, ref))
102+
.filter(usage => Objects.equals(decl, usage.resolve()))
103+
})
104+
}
105+
90106
def resolve(ref: JSReferenceExpression): Option[JSType] = {
91107
// TODO: manually support re-assignment, like
92108
// var someVar = null;
@@ -100,6 +116,17 @@ case class VarRes(ctx: ICtx) {
100116
MultiType.getKey(qualT, keyTOpt)
101117
})
102118

119+
val pushRef = findUsages(ref)
120+
.flatMap(usage => Option(usage.getParent))
121+
.flatMap(cast[JSReferenceExpression](_))
122+
.filter(superRef => "push".equals(superRef.getReferenceName))
123+
.flatMap(psi => Option(psi.getParent))
124+
.flatMap(cast[JSCallExpression](_))
125+
.flatMap(call => call.getArguments.lift(0))
126+
.filter(arg => Lang.log("arg " + arg.getText + " " + arg.getClass))
127+
.flatMap(value => ctx.findExprType(value))
128+
.map(elT => new JSArrayTypeImpl(elT, JSTypeSource.EMPTY))
129+
103130
val briefRef = Option(ref.resolve())
104131
.flatMap(psi => psi match {
105132
case para: JSParameter => resolveArg(para)
@@ -128,6 +155,6 @@ case class VarRes(ctx: ICtx) {
128155
println("Unsupported var declaration - " + psi.getClass + " " + psi.getText)
129156
None
130157
})
131-
MultiType.mergeTypes(deepRef ++ briefRef)
158+
MultiType.mergeTypes(deepRef ++ pushRef ++ briefRef)
132159
}
133160
}

src/org/klesun/lang/Lang.scala

+19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package org.klesun.lang
22

33
import com.intellij.psi.PsiElement
44

5+
import com.intellij.psi.PsiElement
6+
57
import scala.reflect.{ClassTag, classTag}
68
import scala.reflect.runtime.universe._
79

@@ -70,4 +72,21 @@ object Lang {
7072
val lines = text.split("\n").map(l => l.trim)
7173
substr(lines.mkString(" "), 0, length)
7274
}
75+
76+
def findParent[T <: PsiElement : ClassTag](psi: PsiElement): Option[T] = {
77+
var parent = psi.getParent
78+
var matching: Option[T] = None
79+
while (parent != null && matching.isEmpty) {
80+
matching = cast[T](parent)
81+
parent = parent.getParent
82+
}
83+
matching
84+
}
85+
86+
def findChildren[T <: PsiElement : ClassTag](parent: PsiElement): List[T] = {
87+
parent.getChildren
88+
.flatMap(c => findChildren[T](c))
89+
.++(List(parent).flatMap(cast[T](_)))
90+
.toList
91+
}
7392
}

tests/index.js

+43
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,49 @@
22

33
let ssrLineNumbers = [];
44

5+
let collectSmfNotes = function(smf) {
6+
let notes = [];
7+
notes.push({zalupa:123});
8+
let otherEvents = [];
9+
let chanToNotes = range(0,16).map(i => []);
10+
for (let [i, track] of Object.entries(smf.tracks)) {
11+
let time = 0;
12+
let noteOnIndex = 0;
13+
for (let event of track.events) {
14+
time += event.delta;
15+
if (isNoteOn(event)) {
16+
let chan = event.midiChannel;
17+
let tone = event.parameter1;
18+
let velo = event.parameter2;
19+
let dura = 0;
20+
chanToNotes[chan][tone] = chanToNotes[chan][tone] || [];
21+
chanToNotes[chan][tone].push({
22+
tone, time, dura, chan, track: i,
23+
velo, index: noteOnIndex++,
24+
});
25+
} else if (isNoteOff(event)) {
26+
let chan = event.midiChannel;
27+
let tone = event.parameter1;
28+
for (let note of chanToNotes[chan][tone] || []) {
29+
note.dura = time - note.time;
30+
notes.push(note);
31+
}
32+
chanToNotes[chan][tone] = [];
33+
} else {
34+
otherEvents.push({time, event, track: i});
35+
}
36+
}
37+
}
38+
notes[0].a;
39+
return {notes, otherEvents};
40+
};
41+
42+
let testPushInference = function() {
43+
let collected = collectSmfNotes();
44+
// should suggest: zalupa, dura, tone, time, chan, track, velo, index
45+
collected.notes[0].z;
46+
};
47+
548
let testMiscStuff = function() {
649
let getObj = function () {
750
return {

0 commit comments

Comments
 (0)