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