(srv, localFiles, name)
| 434 | } |
| 435 | |
| 436 | function resolveFile(srv, localFiles, name) { |
| 437 | var isRef = name.match(/^#(\d+)$/); |
| 438 | if (!isRef) return srv.findFile(name); |
| 439 | |
| 440 | var file = localFiles[isRef[1]]; |
| 441 | if (!file || file.type == "delete") throw ternError("Reference to unknown file " + name); |
| 442 | if (file.type == "full") return srv.fileMap[file.name]; |
| 443 | |
| 444 | // This is a partial file |
| 445 | |
| 446 | var realFile = file.backing = srv.fileMap[file.name]; |
| 447 | var offset = resolvePos(realFile, file.offsetLines == null ? file.offset : {line: file.offsetLines, ch: 0}, true); |
| 448 | var line = firstLine(file.text); |
| 449 | var foundPos = findMatchingPosition(line, realFile.text, offset); |
| 450 | var pos = foundPos == null ? Math.max(0, realFile.text.lastIndexOf("\n", offset)) : foundPos; |
| 451 | var inObject, atFunction; |
| 452 | |
| 453 | infer.withContext(srv.cx, function() { |
| 454 | infer.purge(file.name, pos, pos + file.text.length); |
| 455 | |
| 456 | var text = file.text, m; |
| 457 | if (m = text.match(/(?:"([^"]*)"|([\w$]+))\s*:\s*function\b/)) { |
| 458 | var objNode = walk.findNodeAround(file.backing.ast, pos, "ObjectExpression"); |
| 459 | if (objNode && objNode.node.objType) |
| 460 | inObject = {type: objNode.node.objType, prop: m[2] || m[1]}; |
| 461 | } |
| 462 | if (foundPos && (m = line.match(/^(.*?)\bfunction\b/))) { |
| 463 | var cut = m[1].length, white = ""; |
| 464 | for (var i = 0; i < cut; ++i) white += " "; |
| 465 | file.text = white + text.slice(cut); |
| 466 | atFunction = true; |
| 467 | } |
| 468 | |
| 469 | var scopeStart = infer.scopeAt(realFile.ast, pos, realFile.scope); |
| 470 | var scopeEnd = infer.scopeAt(realFile.ast, pos + text.length, realFile.scope); |
| 471 | var scope = file.scope = scopeDepth(scopeStart) < scopeDepth(scopeEnd) ? scopeEnd : scopeStart; |
| 472 | file.ast = parseFile(srv, file); |
| 473 | infer.analyze(file.ast, file.name, scope); |
| 474 | |
| 475 | // This is a kludge to tie together the function types (if any) |
| 476 | // outside and inside of the fragment, so that arguments and |
| 477 | // return values have some information known about them. |
| 478 | tieTogether: { |
| 479 | if (inObject || atFunction) { |
| 480 | var newInner = infer.scopeAt(file.ast, line.length, scopeStart); |
| 481 | if (!newInner.fnType) break tieTogether; |
| 482 | if (inObject) { |
| 483 | var prop = inObject.type.getProp(inObject.prop); |
| 484 | prop.addType(newInner.fnType); |
| 485 | } else if (atFunction) { |
| 486 | var inner = infer.scopeAt(realFile.ast, pos + line.length, realFile.scope); |
| 487 | if (inner == scopeStart || !inner.fnType) break tieTogether; |
| 488 | var fOld = inner.fnType, fNew = newInner.fnType; |
| 489 | if (!fNew || (fNew.name != fOld.name && fOld.name)) break tieTogether; |
| 490 | for (var i = 0, e = Math.min(fOld.args.length, fNew.args.length); i < e; ++i) |
| 491 | fOld.args[i].propagate(fNew.args[i]); |
| 492 | fOld.self.propagate(fNew.self); |
| 493 | fNew.retval.propagate(fOld.retval); |
no test coverage detected
searching dependent graphs…