| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750 | // CodeMirror, copyright (c) by Marijn Haverbeke and others// Distributed under an MIT license: https://codemirror.net/LICENSE// Glue code between CodeMirror and Tern.//// Create a CodeMirror.TernServer to wrap an actual Tern server,// register open documents (CodeMirror.Doc instances) with it, and// call its methods to activate the assisting functions that Tern// provides.//// Options supported (all optional):// * defs: An array of JSON definition data structures.// * plugins: An object mapping plugin names to configuration//   options.// * getFile: A function(name, c) that can be used to access files in//   the project that haven't been loaded yet. Simply do c(null) to//   indicate that a file is not available.// * fileFilter: A function(value, docName, doc) that will be applied//   to documents before passing them on to Tern.// * switchToDoc: A function(name, doc) that should, when providing a//   multi-file view, switch the view or focus to the named file.// * showError: A function(editor, message) that can be used to//   override the way errors are displayed.// * completionTip: Customize the content in tooltips for completions.//   Is passed a single argument—the completion's data as returned by//   Tern—and may return a string, DOM node, or null to indicate that//   no tip should be shown. By default the docstring is shown.// * typeTip: Like completionTip, but for the tooltips shown for type//   queries.// * responseFilter: A function(doc, query, request, error, data) that//   will be applied to the Tern responses before treating them////// It is possible to run the Tern server in a web worker by specifying// these additional options:// * useWorker: Set to true to enable web worker mode. You'll probably//   want to feature detect the actual value you use here, for example//   !!window.Worker.// * workerScript: The main script of the worker. Point this to//   wherever you are hosting worker.js from this directory.// * workerDeps: An array of paths pointing (relative to workerScript)//   to the Acorn and Tern libraries and any Tern plugins you want to//   load. Or, if you minified those into a single script and included//   them in the workerScript, simply leave this undefined.(function(mod) {  if (typeof exports == "object" && typeof module == "object") // CommonJS    mod(require("../../lib/codemirror"));  else if (typeof define == "function" && define.amd) // AMD    define(["../../lib/codemirror"], mod);  else // Plain browser env    mod(CodeMirror);})(function(CodeMirror) {  "use strict";  // declare global: tern  CodeMirror.TernServer = function(options) {    var self = this;    this.options = options || {};    var plugins = this.options.plugins || (this.options.plugins = {});    if (!plugins.doc_comment) plugins.doc_comment = true;    this.docs = Object.create(null);    if (this.options.useWorker) {      this.server = new WorkerServer(this);    } else {      this.server = new tern.Server({        getFile: function(name, c) { return getFile(self, name, c); },        async: true,        defs: this.options.defs || [],        plugins: plugins      });    }    this.trackChange = function(doc, change) { trackChange(self, doc, change); };    this.cachedArgHints = null;    this.activeArgHints = null;    this.jumpStack = [];    this.getHint = function(cm, c) { return hint(self, cm, c); };    this.getHint.async = true;  };  CodeMirror.TernServer.prototype = {    addDoc: function(name, doc) {      var data = {doc: doc, name: name, changed: null};      this.server.addFile(name, docValue(this, data));      CodeMirror.on(doc, "change", this.trackChange);      return this.docs[name] = data;    },    delDoc: function(id) {      var found = resolveDoc(this, id);      if (!found) return;      CodeMirror.off(found.doc, "change", this.trackChange);      delete this.docs[found.name];      this.server.delFile(found.name);    },    hideDoc: function(id) {      closeArgHints(this);      var found = resolveDoc(this, id);      if (found && found.changed) sendDoc(this, found);    },    complete: function(cm) {      cm.showHint({hint: this.getHint});    },    showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); },    showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); },    updateArgHints: function(cm) { updateArgHints(this, cm); },    jumpToDef: function(cm) { jumpToDef(this, cm); },    jumpBack: function(cm) { jumpBack(this, cm); },    rename: function(cm) { rename(this, cm); },    selectName: function(cm) { selectName(this, cm); },    request: function (cm, query, c, pos) {      var self = this;      var doc = findDoc(this, cm.getDoc());      var request = buildRequest(this, doc, query, pos);      var extraOptions = request.query && this.options.queryOptions && this.options.queryOptions[request.query.type]      if (extraOptions) for (var prop in extraOptions) request.query[prop] = extraOptions[prop];      this.server.request(request, function (error, data) {        if (!error && self.options.responseFilter)          data = self.options.responseFilter(doc, query, request, error, data);        c(error, data);      });    },    destroy: function () {      closeArgHints(this)      if (this.worker) {        this.worker.terminate();        this.worker = null;      }    }  };  var Pos = CodeMirror.Pos;  var cls = "CodeMirror-Tern-";  var bigDoc = 250;  function getFile(ts, name, c) {    var buf = ts.docs[name];    if (buf)      c(docValue(ts, buf));    else if (ts.options.getFile)      ts.options.getFile(name, c);    else      c(null);  }  function findDoc(ts, doc, name) {    for (var n in ts.docs) {      var cur = ts.docs[n];      if (cur.doc == doc) return cur;    }    if (!name) for (var i = 0;; ++i) {      n = "[doc" + (i || "") + "]";      if (!ts.docs[n]) { name = n; break; }    }    return ts.addDoc(name, doc);  }  function resolveDoc(ts, id) {    if (typeof id == "string") return ts.docs[id];    if (id instanceof CodeMirror) id = id.getDoc();    if (id instanceof CodeMirror.Doc) return findDoc(ts, id);  }  function trackChange(ts, doc, change) {    var data = findDoc(ts, doc);    var argHints = ts.cachedArgHints;    if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) >= 0)      ts.cachedArgHints = null;    var changed = data.changed;    if (changed == null)      data.changed = changed = {from: change.from.line, to: change.from.line};    var end = change.from.line + (change.text.length - 1);    if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);    if (end >= changed.to) changed.to = end + 1;    if (changed.from > change.from.line) changed.from = change.from.line;    if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {      if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);    }, 200);  }  function sendDoc(ts, doc) {    ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) {      if (error) window.console.error(error);      else doc.changed = null;    });  }  // Completion  function hint(ts, cm, c) {    ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) {      if (error) return showError(ts, cm, error);      var completions = [], after = "";      var from = data.start, to = data.end;      if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" &&          cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]")        after = "\"]";      for (var i = 0; i < data.completions.length; ++i) {        var completion = data.completions[i], className = typeToIcon(completion.type);        if (data.guess) className += " " + cls + "guess";        completions.push({text: completion.name + after,                          displayText: completion.displayName || completion.name,                          className: className,                          data: completion});      }      var obj = {from: from, to: to, list: completions};      var tooltip = null;      CodeMirror.on(obj, "close", function() { remove(tooltip); });      CodeMirror.on(obj, "update", function() { remove(tooltip); });      CodeMirror.on(obj, "select", function(cur, node) {        remove(tooltip);        var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;        if (content) {          tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,                                node.getBoundingClientRect().top + window.pageYOffset, content, cm, cls + "hint-doc");        }      });      c(obj);    });  }  function typeToIcon(type) {    var suffix;    if (type == "?") suffix = "unknown";    else if (type == "number" || type == "string" || type == "bool") suffix = type;    else if (/^fn\(/.test(type)) suffix = "fn";    else if (/^\[/.test(type)) suffix = "array";    else suffix = "object";    return cls + "completion " + cls + "completion-" + suffix;  }  // Type queries  function showContextInfo(ts, cm, pos, queryName, c) {    ts.request(cm, queryName, function(error, data) {      if (error) return showError(ts, cm, error);      if (ts.options.typeTip) {        var tip = ts.options.typeTip(data);      } else {        var tip = elt("span", null, elt("strong", null, data.type || "not found"));        if (data.doc)          tip.appendChild(document.createTextNode(" — " + data.doc));        if (data.url) {          tip.appendChild(document.createTextNode(" "));          var child = tip.appendChild(elt("a", null, "[docs]"));          child.href = data.url;          child.target = "_blank";        }      }      tempTooltip(cm, tip, ts);      if (c) c();    }, pos);  }  // Maintaining argument hints  function updateArgHints(ts, cm) {    closeArgHints(ts);    if (cm.somethingSelected()) return;    var state = cm.getTokenAt(cm.getCursor()).state;    var inner = CodeMirror.innerMode(cm.getMode(), state);    if (inner.mode.name != "javascript") return;    var lex = inner.state.lexical;    if (lex.info != "call") return;    var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize");    for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {      var str = cm.getLine(line), extra = 0;      for (var pos = 0;;) {        var tab = str.indexOf("\t", pos);        if (tab == -1) break;        extra += tabSize - (tab + extra) % tabSize - 1;        pos = tab + 1;      }      ch = lex.column - extra;      if (str.charAt(ch) == "(") {found = true; break;}    }    if (!found) return;    var start = Pos(line, ch);    var cache = ts.cachedArgHints;    if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)      return showArgHints(ts, cm, argPos);    ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) {      if (error || !data.type || !(/^fn\(/).test(data.type)) return;      ts.cachedArgHints = {        start: start,        type: parseFnType(data.type),        name: data.exprName || data.name || "fn",        guess: data.guess,        doc: cm.getDoc()      };      showArgHints(ts, cm, argPos);    });  }  function showArgHints(ts, cm, pos) {    closeArgHints(ts);    var cache = ts.cachedArgHints, tp = cache.type;    var tip = elt("span", cache.guess ? cls + "fhint-guess" : null,                  elt("span", cls + "fname", cache.name), "(");    for (var i = 0; i < tp.args.length; ++i) {      if (i) tip.appendChild(document.createTextNode(", "));      var arg = tp.args[i];      tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?"));      if (arg.type != "?") {        tip.appendChild(document.createTextNode(":\u00a0"));        tip.appendChild(elt("span", cls + "type", arg.type));      }    }    tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));    if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));    var place = cm.cursorCoords(null, "page");    var tooltip = ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip, cm)    setTimeout(function() {      tooltip.clear = onEditorActivity(cm, function() {        if (ts.activeArgHints == tooltip) closeArgHints(ts) })    }, 20)  }  function parseFnType(text) {    var args = [], pos = 3;    function skipMatching(upto) {      var depth = 0, start = pos;      for (;;) {        var next = text.charAt(pos);        if (upto.test(next) && !depth) return text.slice(start, pos);        if (/[{\[\(]/.test(next)) ++depth;        else if (/[}\]\)]/.test(next)) --depth;        ++pos;      }    }    // Parse arguments    if (text.charAt(pos) != ")") for (;;) {      var name = text.slice(pos).match(/^([^, \(\[\{]+): /);      if (name) {        pos += name[0].length;        name = name[1];      }      args.push({name: name, type: skipMatching(/[\),]/)});      if (text.charAt(pos) == ")") break;      pos += 2;    }    var rettype = text.slice(pos).match(/^\) -> (.*)$/);    return {args: args, rettype: rettype && rettype[1]};  }  // Moving to the definition of something  function jumpToDef(ts, cm) {    function inner(varName) {      var req = {type: "definition", variable: varName || null};      var doc = findDoc(ts, cm.getDoc());      ts.server.request(buildRequest(ts, doc, req), function(error, data) {        if (error) return showError(ts, cm, error);        if (!data.file && data.url) { window.open(data.url); return; }        if (data.file) {          var localDoc = ts.docs[data.file], found;          if (localDoc && (found = findContext(localDoc.doc, data))) {            ts.jumpStack.push({file: doc.name,                               start: cm.getCursor("from"),                               end: cm.getCursor("to")});            moveTo(ts, doc, localDoc, found.start, found.end);            return;          }        }        showError(ts, cm, "Could not find a definition.");      });    }    if (!atInterestingExpression(cm))      dialog(cm, "Jump to variable", function(name) { if (name) inner(name); });    else      inner();  }  function jumpBack(ts, cm) {    var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];    if (!doc) return;    moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);  }  function moveTo(ts, curDoc, doc, start, end) {    doc.doc.setSelection(start, end);    if (curDoc != doc && ts.options.switchToDoc) {      closeArgHints(ts);      ts.options.switchToDoc(doc.name, doc.doc);    }  }  // The {line,ch} representation of positions makes this rather awkward.  function findContext(doc, data) {    var before = data.context.slice(0, data.contextOffset).split("\n");    var startLine = data.start.line - (before.length - 1);    var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);    var text = doc.getLine(startLine).slice(start.ch);    for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)      text += "\n" + doc.getLine(cur);    if (text.slice(0, data.context.length) == data.context) return data;    var cursor = doc.getSearchCursor(data.context, 0, false);    var nearest, nearestDist = Infinity;    while (cursor.findNext()) {      var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;      if (!dist) dist = Math.abs(from.ch - start.ch);      if (dist < nearestDist) { nearest = from; nearestDist = dist; }    }    if (!nearest) return null;    if (before.length == 1)      nearest.ch += before[0].length;    else      nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);    if (data.start.line == data.end.line)      var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));    else      var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);    return {start: nearest, end: end};  }  function atInterestingExpression(cm) {    var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos);    if (tok.start < pos.ch && tok.type == "comment") return false;    return /[\w)\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));  }  // Variable renaming  function rename(ts, cm) {    var token = cm.getTokenAt(cm.getCursor());    if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable");    dialog(cm, "New name for " + token.string, function(newName) {      ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) {        if (error) return showError(ts, cm, error);        applyChanges(ts, data.changes);      });    });  }  function selectName(ts, cm) {    var name = findDoc(ts, cm.doc).name;    ts.request(cm, {type: "refs"}, function(error, data) {      if (error) return showError(ts, cm, error);      var ranges = [], cur = 0;      var curPos = cm.getCursor();      for (var i = 0; i < data.refs.length; i++) {        var ref = data.refs[i];        if (ref.file == name) {          ranges.push({anchor: ref.start, head: ref.end});          if (cmpPos(curPos, ref.start) >= 0 && cmpPos(curPos, ref.end) <= 0)            cur = ranges.length - 1;        }      }      cm.setSelections(ranges, cur);    });  }  var nextChangeOrig = 0;  function applyChanges(ts, changes) {    var perFile = Object.create(null);    for (var i = 0; i < changes.length; ++i) {      var ch = changes[i];      (perFile[ch.file] || (perFile[ch.file] = [])).push(ch);    }    for (var file in perFile) {      var known = ts.docs[file], chs = perFile[file];;      if (!known) continue;      chs.sort(function(a, b) { return cmpPos(b.start, a.start); });      var origin = "*rename" + (++nextChangeOrig);      for (var i = 0; i < chs.length; ++i) {        var ch = chs[i];        known.doc.replaceRange(ch.text, ch.start, ch.end, origin);      }    }  }  // Generic request-building helper  function buildRequest(ts, doc, query, pos) {    var files = [], offsetLines = 0, allowFragments = !query.fullDocs;    if (!allowFragments) delete query.fullDocs;    if (typeof query == "string") query = {type: query};    query.lineCharPositions = true;    if (query.end == null) {      query.end = pos || doc.doc.getCursor("end");      if (doc.doc.somethingSelected())        query.start = doc.doc.getCursor("start");    }    var startPos = query.start || query.end;    if (doc.changed) {      if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&          doc.changed.to - doc.changed.from < 100 &&          doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {        files.push(getFragmentAround(doc, startPos, query.end));        query.file = "#0";        var offsetLines = files[0].offsetLines;        if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);        query.end = Pos(query.end.line - offsetLines, query.end.ch);      } else {        files.push({type: "full",                    name: doc.name,                    text: docValue(ts, doc)});        query.file = doc.name;        doc.changed = null;      }    } else {      query.file = doc.name;    }    for (var name in ts.docs) {      var cur = ts.docs[name];      if (cur.changed && cur != doc) {        files.push({type: "full", name: cur.name, text: docValue(ts, cur)});        cur.changed = null;      }    }    return {query: query, files: files};  }  function getFragmentAround(data, start, end) {    var doc = data.doc;    var minIndent = null, minLine = null, endLine, tabSize = 4;    for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {      var line = doc.getLine(p), fn = line.search(/\bfunction\b/);      if (fn < 0) continue;      var indent = CodeMirror.countColumn(line, null, tabSize);      if (minIndent != null && minIndent <= indent) continue;      minIndent = indent;      minLine = p;    }    if (minLine == null) minLine = min;    var max = Math.min(doc.lastLine(), end.line + 20);    if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))      endLine = max;    else for (endLine = end.line + 1; endLine < max; ++endLine) {      var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);      if (indent <= minIndent) break;    }    var from = Pos(minLine, 0);    return {type: "part",            name: data.name,            offsetLines: from.line,            text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0))};  }  // Generic utilities  var cmpPos = CodeMirror.cmpPos;  function elt(tagname, cls /*, ... elts*/) {    var e = document.createElement(tagname);    if (cls) e.className = cls;    for (var i = 2; i < arguments.length; ++i) {      var elt = arguments[i];      if (typeof elt == "string") elt = document.createTextNode(elt);      e.appendChild(elt);    }    return e;  }  function dialog(cm, text, f) {    if (cm.openDialog)      cm.openDialog(text + ": <input type=text>", f);    else      f(prompt(text, ""));  }  // Tooltips  function tempTooltip(cm, content, ts) {    if (cm.state.ternTooltip) remove(cm.state.ternTooltip);    var where = cm.cursorCoords();    var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content, cm);    function maybeClear() {      old = true;      if (!mouseOnTip) clear();    }    function clear() {      cm.state.ternTooltip = null;      if (tip.parentNode) fadeOut(tip)      clearActivity()    }    var mouseOnTip = false, old = false;    CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });    CodeMirror.on(tip, "mouseout", function(e) {      var related = e.relatedTarget || e.toElement      if (!related || !CodeMirror.contains(tip, related)) {        if (old) clear();        else mouseOnTip = false;      }    });    setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700);    var clearActivity = onEditorActivity(cm, clear)  }  function onEditorActivity(cm, f) {    cm.on("cursorActivity", f)    cm.on("blur", f)    cm.on("scroll", f)    cm.on("setDoc", f)    return function() {      cm.off("cursorActivity", f)      cm.off("blur", f)      cm.off("scroll", f)      cm.off("setDoc", f)    }  }  function makeTooltip(x, y, content, cm, className) {    var node = elt("div", cls + "tooltip" + " " + (className || ""), content);    node.style.left = x + "px";    node.style.top = y + "px";    var container = ((cm.options || {}).hintOptions || {}).container || document.body;    container.appendChild(node);    var pos = cm.cursorCoords();    var winW = window.innerWidth;    var winH = window.innerHeight;    var box = node.getBoundingClientRect();    var hints = document.querySelector(".CodeMirror-hints");    var overlapY = box.bottom - winH;    var overlapX = box.right - winW;    if (hints && overlapX > 0) {      node.style.left = 0;      var box = node.getBoundingClientRect();      node.style.left = (x = x - hints.offsetWidth - box.width) + "px";      overlapX = box.right - winW;    }    if (overlapY > 0) {      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);      if (curTop - height > 0) { // Fits above cursor        node.style.top = (pos.top - height) + "px";      } else if (height > winH) {        node.style.height = (winH - 5) + "px";        node.style.top = (pos.bottom - box.top) + "px";      }    }    if (overlapX > 0) {      if (box.right - box.left > winW) {        node.style.width = (winW - 5) + "px";        overlapX -= (box.right - box.left) - winW;      }      node.style.left = (x - overlapX) + "px";    }    return node;  }  function remove(node) {    var p = node && node.parentNode;    if (p) p.removeChild(node);  }  function fadeOut(tooltip) {    tooltip.style.opacity = "0";    setTimeout(function() { remove(tooltip); }, 1100);  }  function showError(ts, cm, msg) {    if (ts.options.showError)      ts.options.showError(cm, msg);    else      tempTooltip(cm, String(msg), ts);  }  function closeArgHints(ts) {    if (ts.activeArgHints) {      if (ts.activeArgHints.clear) ts.activeArgHints.clear()      remove(ts.activeArgHints)      ts.activeArgHints = null    }  }  function docValue(ts, doc) {    var val = doc.doc.getValue();    if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);    return val;  }  // Worker wrapper  function WorkerServer(ts) {    var worker = ts.worker = new Worker(ts.options.workerScript);    worker.postMessage({type: "init",                        defs: ts.options.defs,                        plugins: ts.options.plugins,                        scripts: ts.options.workerDeps});    var msgId = 0, pending = {};    function send(data, c) {      if (c) {        data.id = ++msgId;        pending[msgId] = c;      }      worker.postMessage(data);    }    worker.onmessage = function(e) {      var data = e.data;      if (data.type == "getFile") {        getFile(ts, data.name, function(err, text) {          send({type: "getFile", err: String(err), text: text, id: data.id});        });      } else if (data.type == "debug") {        window.console.log(data.message);      } else if (data.id && pending[data.id]) {        pending[data.id](data.err, data.body);        delete pending[data.id];      }    };    worker.onerror = function(e) {      for (var id in pending) pending[id](e);      pending = {};    };    this.addFile = function(name, text) { send({type: "add", name: name, text: text}); };    this.delFile = function(name) { send({type: "del", name: name}); };    this.request = function(body, c) { send({type: "req", body: body}, c); };  }});
 |