| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719 | 
							- // 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);
 
-           tooltip.className += " " + 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) {
 
-     var node = elt("div", cls + "tooltip", content);
 
-     node.style.left = x + "px";
 
-     node.style.top = y + "px";
 
-     var container = ((cm.options || {}).hintOptions || {}).container || document.body;
 
-     container.appendChild(node);
 
-     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); };
 
-   }
 
- });
 
 
  |