1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | (function(mod) {
|
47 | if (typeof exports == "object" && typeof module == "object")
|
48 | mod(require("../../lib/codemirror"));
|
49 | else if (typeof define == "function" && define.amd)
|
50 | define(["../../lib/codemirror"], mod);
|
51 | else
|
52 | mod(CodeMirror);
|
53 | })(function(CodeMirror) {
|
54 | "use strict";
|
55 |
|
56 |
|
57 | CodeMirror.TernServer = function(options) {
|
58 | var self = this;
|
59 | this.options = options || {};
|
60 | var plugins = this.options.plugins || (this.options.plugins = {});
|
61 | if (!plugins.doc_comment) plugins.doc_comment = true;
|
62 | this.docs = Object.create(null);
|
63 | if (this.options.useWorker) {
|
64 | this.server = new WorkerServer(this);
|
65 | } else {
|
66 | this.server = new tern.Server({
|
67 | getFile: function(name, c) { return getFile(self, name, c); },
|
68 | async: true,
|
69 | defs: this.options.defs || [],
|
70 | plugins: plugins
|
71 | });
|
72 | }
|
73 | this.trackChange = function(doc, change) { trackChange(self, doc, change); };
|
74 |
|
75 | this.cachedArgHints = null;
|
76 | this.activeArgHints = null;
|
77 | this.jumpStack = [];
|
78 |
|
79 | this.getHint = function(cm, c) { return hint(self, cm, c); };
|
80 | this.getHint.async = true;
|
81 | };
|
82 |
|
83 | CodeMirror.TernServer.prototype = {
|
84 | addDoc: function(name, doc) {
|
85 | var data = {doc: doc, name: name, changed: null};
|
86 | this.server.addFile(name, docValue(this, data));
|
87 | CodeMirror.on(doc, "change", this.trackChange);
|
88 | return this.docs[name] = data;
|
89 | },
|
90 |
|
91 | delDoc: function(id) {
|
92 | var found = resolveDoc(this, id);
|
93 | if (!found) return;
|
94 | CodeMirror.off(found.doc, "change", this.trackChange);
|
95 | delete this.docs[found.name];
|
96 | this.server.delFile(found.name);
|
97 | },
|
98 |
|
99 | hideDoc: function(id) {
|
100 | closeArgHints(this);
|
101 | var found = resolveDoc(this, id);
|
102 | if (found && found.changed) sendDoc(this, found);
|
103 | },
|
104 |
|
105 | complete: function(cm) {
|
106 | cm.showHint({hint: this.getHint});
|
107 | },
|
108 |
|
109 | showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); },
|
110 |
|
111 | showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); },
|
112 |
|
113 | updateArgHints: function(cm) { updateArgHints(this, cm); },
|
114 |
|
115 | jumpToDef: function(cm) { jumpToDef(this, cm); },
|
116 |
|
117 | jumpBack: function(cm) { jumpBack(this, cm); },
|
118 |
|
119 | rename: function(cm) { rename(this, cm); },
|
120 |
|
121 | selectName: function(cm) { selectName(this, cm); },
|
122 |
|
123 | request: function (cm, query, c, pos) {
|
124 | var self = this;
|
125 | var doc = findDoc(this, cm.getDoc());
|
126 | var request = buildRequest(this, doc, query, pos);
|
127 | var extraOptions = request.query && this.options.queryOptions && this.options.queryOptions[request.query.type]
|
128 | if (extraOptions) for (var prop in extraOptions) request.query[prop] = extraOptions[prop];
|
129 |
|
130 | this.server.request(request, function (error, data) {
|
131 | if (!error && self.options.responseFilter)
|
132 | data = self.options.responseFilter(doc, query, request, error, data);
|
133 | c(error, data);
|
134 | });
|
135 | },
|
136 |
|
137 | destroy: function () {
|
138 | closeArgHints(this)
|
139 | if (this.worker) {
|
140 | this.worker.terminate();
|
141 | this.worker = null;
|
142 | }
|
143 | }
|
144 | };
|
145 |
|
146 | var Pos = CodeMirror.Pos;
|
147 | var cls = "CodeMirror-Tern-";
|
148 | var bigDoc = 250;
|
149 |
|
150 | function getFile(ts, name, c) {
|
151 | var buf = ts.docs[name];
|
152 | if (buf)
|
153 | c(docValue(ts, buf));
|
154 | else if (ts.options.getFile)
|
155 | ts.options.getFile(name, c);
|
156 | else
|
157 | c(null);
|
158 | }
|
159 |
|
160 | function findDoc(ts, doc, name) {
|
161 | for (var n in ts.docs) {
|
162 | var cur = ts.docs[n];
|
163 | if (cur.doc == doc) return cur;
|
164 | }
|
165 | if (!name) for (var i = 0;; ++i) {
|
166 | n = "[doc" + (i || "") + "]";
|
167 | if (!ts.docs[n]) { name = n; break; }
|
168 | }
|
169 | return ts.addDoc(name, doc);
|
170 | }
|
171 |
|
172 | function resolveDoc(ts, id) {
|
173 | if (typeof id == "string") return ts.docs[id];
|
174 | if (id instanceof CodeMirror) id = id.getDoc();
|
175 | if (id instanceof CodeMirror.Doc) return findDoc(ts, id);
|
176 | }
|
177 |
|
178 | function trackChange(ts, doc, change) {
|
179 | var data = findDoc(ts, doc);
|
180 |
|
181 | var argHints = ts.cachedArgHints;
|
182 | if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) >= 0)
|
183 | ts.cachedArgHints = null;
|
184 |
|
185 | var changed = data.changed;
|
186 | if (changed == null)
|
187 | data.changed = changed = {from: change.from.line, to: change.from.line};
|
188 | var end = change.from.line + (change.text.length - 1);
|
189 | if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);
|
190 | if (end >= changed.to) changed.to = end + 1;
|
191 | if (changed.from > change.from.line) changed.from = change.from.line;
|
192 |
|
193 | if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {
|
194 | if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);
|
195 | }, 200);
|
196 | }
|
197 |
|
198 | function sendDoc(ts, doc) {
|
199 | ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) {
|
200 | if (error) window.console.error(error);
|
201 | else doc.changed = null;
|
202 | });
|
203 | }
|
204 |
|
205 |
|
206 |
|
207 | function hint(ts, cm, c) {
|
208 | ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) {
|
209 | if (error) return showError(ts, cm, error);
|
210 | var completions = [], after = "";
|
211 | var from = data.start, to = data.end;
|
212 | if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" &&
|
213 | cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]")
|
214 | after = "\"]";
|
215 |
|
216 | for (var i = 0; i < data.completions.length; ++i) {
|
217 | var completion = data.completions[i], className = typeToIcon(completion.type);
|
218 | if (data.guess) className += " " + cls + "guess";
|
219 | completions.push({text: completion.name + after,
|
220 | displayText: completion.displayName || completion.name,
|
221 | className: className,
|
222 | data: completion});
|
223 | }
|
224 |
|
225 | var obj = {from: from, to: to, list: completions};
|
226 | var tooltip = null;
|
227 | CodeMirror.on(obj, "close", function() { remove(tooltip); });
|
228 | CodeMirror.on(obj, "update", function() { remove(tooltip); });
|
229 | CodeMirror.on(obj, "select", function(cur, node) {
|
230 | remove(tooltip);
|
231 | var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;
|
232 | if (content) {
|
233 | tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,
|
234 | node.getBoundingClientRect().top + window.pageYOffset, content, cm);
|
235 | tooltip.className += " " + cls + "hint-doc";
|
236 | }
|
237 | });
|
238 | c(obj);
|
239 | });
|
240 | }
|
241 |
|
242 | function typeToIcon(type) {
|
243 | var suffix;
|
244 | if (type == "?") suffix = "unknown";
|
245 | else if (type == "number" || type == "string" || type == "bool") suffix = type;
|
246 | else if (/^fn\(/.test(type)) suffix = "fn";
|
247 | else if (/^\[/.test(type)) suffix = "array";
|
248 | else suffix = "object";
|
249 | return cls + "completion " + cls + "completion-" + suffix;
|
250 | }
|
251 |
|
252 |
|
253 |
|
254 | function showContextInfo(ts, cm, pos, queryName, c) {
|
255 | ts.request(cm, queryName, function(error, data) {
|
256 | if (error) return showError(ts, cm, error);
|
257 | if (ts.options.typeTip) {
|
258 | var tip = ts.options.typeTip(data);
|
259 | } else {
|
260 | var tip = elt("span", null, elt("strong", null, data.type || "not found"));
|
261 | if (data.doc)
|
262 | tip.appendChild(document.createTextNode(" — " + data.doc));
|
263 | if (data.url) {
|
264 | tip.appendChild(document.createTextNode(" "));
|
265 | var child = tip.appendChild(elt("a", null, "[docs]"));
|
266 | child.href = data.url;
|
267 | child.target = "_blank";
|
268 | }
|
269 | }
|
270 | tempTooltip(cm, tip, ts);
|
271 | if (c) c();
|
272 | }, pos);
|
273 | }
|
274 |
|
275 |
|
276 |
|
277 | function updateArgHints(ts, cm) {
|
278 | closeArgHints(ts);
|
279 |
|
280 | if (cm.somethingSelected()) return;
|
281 | var state = cm.getTokenAt(cm.getCursor()).state;
|
282 | var inner = CodeMirror.innerMode(cm.getMode(), state);
|
283 | if (inner.mode.name != "javascript") return;
|
284 | var lex = inner.state.lexical;
|
285 | if (lex.info != "call") return;
|
286 |
|
287 | var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize");
|
288 | for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
|
289 | var str = cm.getLine(line), extra = 0;
|
290 | for (var pos = 0;;) {
|
291 | var tab = str.indexOf("\t", pos);
|
292 | if (tab == -1) break;
|
293 | extra += tabSize - (tab + extra) % tabSize - 1;
|
294 | pos = tab + 1;
|
295 | }
|
296 | ch = lex.column - extra;
|
297 | if (str.charAt(ch) == "(") {found = true; break;}
|
298 | }
|
299 | if (!found) return;
|
300 |
|
301 | var start = Pos(line, ch);
|
302 | var cache = ts.cachedArgHints;
|
303 | if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)
|
304 | return showArgHints(ts, cm, argPos);
|
305 |
|
306 | ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) {
|
307 | if (error || !data.type || !(/^fn\(/).test(data.type)) return;
|
308 | ts.cachedArgHints = {
|
309 | start: start,
|
310 | type: parseFnType(data.type),
|
311 | name: data.exprName || data.name || "fn",
|
312 | guess: data.guess,
|
313 | doc: cm.getDoc()
|
314 | };
|
315 | showArgHints(ts, cm, argPos);
|
316 | });
|
317 | }
|
318 |
|
319 | function showArgHints(ts, cm, pos) {
|
320 | closeArgHints(ts);
|
321 |
|
322 | var cache = ts.cachedArgHints, tp = cache.type;
|
323 | var tip = elt("span", cache.guess ? cls + "fhint-guess" : null,
|
324 | elt("span", cls + "fname", cache.name), "(");
|
325 | for (var i = 0; i < tp.args.length; ++i) {
|
326 | if (i) tip.appendChild(document.createTextNode(", "));
|
327 | var arg = tp.args[i];
|
328 | tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?"));
|
329 | if (arg.type != "?") {
|
330 | tip.appendChild(document.createTextNode(":\u00a0"));
|
331 | tip.appendChild(elt("span", cls + "type", arg.type));
|
332 | }
|
333 | }
|
334 | tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));
|
335 | if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));
|
336 | var place = cm.cursorCoords(null, "page");
|
337 | var tooltip = ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip, cm)
|
338 | setTimeout(function() {
|
339 | tooltip.clear = onEditorActivity(cm, function() {
|
340 | if (ts.activeArgHints == tooltip) closeArgHints(ts) })
|
341 | }, 20)
|
342 | }
|
343 |
|
344 | function parseFnType(text) {
|
345 | var args = [], pos = 3;
|
346 |
|
347 | function skipMatching(upto) {
|
348 | var depth = 0, start = pos;
|
349 | for (;;) {
|
350 | var next = text.charAt(pos);
|
351 | if (upto.test(next) && !depth) return text.slice(start, pos);
|
352 | if (/[{\[\(]/.test(next)) ++depth;
|
353 | else if (/[}\]\)]/.test(next)) --depth;
|
354 | ++pos;
|
355 | }
|
356 | }
|
357 |
|
358 |
|
359 | if (text.charAt(pos) != ")") for (;;) {
|
360 | var name = text.slice(pos).match(/^([^, \(\[\{]+): /);
|
361 | if (name) {
|
362 | pos += name[0].length;
|
363 | name = name[1];
|
364 | }
|
365 | args.push({name: name, type: skipMatching(/[\),]/)});
|
366 | if (text.charAt(pos) == ")") break;
|
367 | pos += 2;
|
368 | }
|
369 |
|
370 | var rettype = text.slice(pos).match(/^\) -> (.*)$/);
|
371 |
|
372 | return {args: args, rettype: rettype && rettype[1]};
|
373 | }
|
374 |
|
375 |
|
376 |
|
377 | function jumpToDef(ts, cm) {
|
378 | function inner(varName) {
|
379 | var req = {type: "definition", variable: varName || null};
|
380 | var doc = findDoc(ts, cm.getDoc());
|
381 | ts.server.request(buildRequest(ts, doc, req), function(error, data) {
|
382 | if (error) return showError(ts, cm, error);
|
383 | if (!data.file && data.url) { window.open(data.url); return; }
|
384 |
|
385 | if (data.file) {
|
386 | var localDoc = ts.docs[data.file], found;
|
387 | if (localDoc && (found = findContext(localDoc.doc, data))) {
|
388 | ts.jumpStack.push({file: doc.name,
|
389 | start: cm.getCursor("from"),
|
390 | end: cm.getCursor("to")});
|
391 | moveTo(ts, doc, localDoc, found.start, found.end);
|
392 | return;
|
393 | }
|
394 | }
|
395 | showError(ts, cm, "Could not find a definition.");
|
396 | });
|
397 | }
|
398 |
|
399 | if (!atInterestingExpression(cm))
|
400 | dialog(cm, "Jump to variable", function(name) { if (name) inner(name); });
|
401 | else
|
402 | inner();
|
403 | }
|
404 |
|
405 | function jumpBack(ts, cm) {
|
406 | var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];
|
407 | if (!doc) return;
|
408 | moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);
|
409 | }
|
410 |
|
411 | function moveTo(ts, curDoc, doc, start, end) {
|
412 | doc.doc.setSelection(start, end);
|
413 | if (curDoc != doc && ts.options.switchToDoc) {
|
414 | closeArgHints(ts);
|
415 | ts.options.switchToDoc(doc.name, doc.doc);
|
416 | }
|
417 | }
|
418 |
|
419 |
|
420 | function findContext(doc, data) {
|
421 | var before = data.context.slice(0, data.contextOffset).split("\n");
|
422 | var startLine = data.start.line - (before.length - 1);
|
423 | var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);
|
424 |
|
425 | var text = doc.getLine(startLine).slice(start.ch);
|
426 | for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)
|
427 | text += "\n" + doc.getLine(cur);
|
428 | if (text.slice(0, data.context.length) == data.context) return data;
|
429 |
|
430 | var cursor = doc.getSearchCursor(data.context, 0, false);
|
431 | var nearest, nearestDist = Infinity;
|
432 | while (cursor.findNext()) {
|
433 | var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;
|
434 | if (!dist) dist = Math.abs(from.ch - start.ch);
|
435 | if (dist < nearestDist) { nearest = from; nearestDist = dist; }
|
436 | }
|
437 | if (!nearest) return null;
|
438 |
|
439 | if (before.length == 1)
|
440 | nearest.ch += before[0].length;
|
441 | else
|
442 | nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);
|
443 | if (data.start.line == data.end.line)
|
444 | var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));
|
445 | else
|
446 | var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);
|
447 | return {start: nearest, end: end};
|
448 | }
|
449 |
|
450 | function atInterestingExpression(cm) {
|
451 | var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos);
|
452 | if (tok.start < pos.ch && tok.type == "comment") return false;
|
453 | return /[\w)\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));
|
454 | }
|
455 |
|
456 |
|
457 |
|
458 | function rename(ts, cm) {
|
459 | var token = cm.getTokenAt(cm.getCursor());
|
460 | if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable");
|
461 | dialog(cm, "New name for " + token.string, function(newName) {
|
462 | ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) {
|
463 | if (error) return showError(ts, cm, error);
|
464 | applyChanges(ts, data.changes);
|
465 | });
|
466 | });
|
467 | }
|
468 |
|
469 | function selectName(ts, cm) {
|
470 | var name = findDoc(ts, cm.doc).name;
|
471 | ts.request(cm, {type: "refs"}, function(error, data) {
|
472 | if (error) return showError(ts, cm, error);
|
473 | var ranges = [], cur = 0;
|
474 | var curPos = cm.getCursor();
|
475 | for (var i = 0; i < data.refs.length; i++) {
|
476 | var ref = data.refs[i];
|
477 | if (ref.file == name) {
|
478 | ranges.push({anchor: ref.start, head: ref.end});
|
479 | if (cmpPos(curPos, ref.start) >= 0 && cmpPos(curPos, ref.end) <= 0)
|
480 | cur = ranges.length - 1;
|
481 | }
|
482 | }
|
483 | cm.setSelections(ranges, cur);
|
484 | });
|
485 | }
|
486 |
|
487 | var nextChangeOrig = 0;
|
488 | function applyChanges(ts, changes) {
|
489 | var perFile = Object.create(null);
|
490 | for (var i = 0; i < changes.length; ++i) {
|
491 | var ch = changes[i];
|
492 | (perFile[ch.file] || (perFile[ch.file] = [])).push(ch);
|
493 | }
|
494 | for (var file in perFile) {
|
495 | var known = ts.docs[file], chs = perFile[file];;
|
496 | if (!known) continue;
|
497 | chs.sort(function(a, b) { return cmpPos(b.start, a.start); });
|
498 | var origin = "*rename" + (++nextChangeOrig);
|
499 | for (var i = 0; i < chs.length; ++i) {
|
500 | var ch = chs[i];
|
501 | known.doc.replaceRange(ch.text, ch.start, ch.end, origin);
|
502 | }
|
503 | }
|
504 | }
|
505 |
|
506 |
|
507 |
|
508 | function buildRequest(ts, doc, query, pos) {
|
509 | var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
|
510 | if (!allowFragments) delete query.fullDocs;
|
511 | if (typeof query == "string") query = {type: query};
|
512 | query.lineCharPositions = true;
|
513 | if (query.end == null) {
|
514 | query.end = pos || doc.doc.getCursor("end");
|
515 | if (doc.doc.somethingSelected())
|
516 | query.start = doc.doc.getCursor("start");
|
517 | }
|
518 | var startPos = query.start || query.end;
|
519 |
|
520 | if (doc.changed) {
|
521 | if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&
|
522 | doc.changed.to - doc.changed.from < 100 &&
|
523 | doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {
|
524 | files.push(getFragmentAround(doc, startPos, query.end));
|
525 | query.file = "#0";
|
526 | var offsetLines = files[0].offsetLines;
|
527 | if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);
|
528 | query.end = Pos(query.end.line - offsetLines, query.end.ch);
|
529 | } else {
|
530 | files.push({type: "full",
|
531 | name: doc.name,
|
532 | text: docValue(ts, doc)});
|
533 | query.file = doc.name;
|
534 | doc.changed = null;
|
535 | }
|
536 | } else {
|
537 | query.file = doc.name;
|
538 | }
|
539 | for (var name in ts.docs) {
|
540 | var cur = ts.docs[name];
|
541 | if (cur.changed && cur != doc) {
|
542 | files.push({type: "full", name: cur.name, text: docValue(ts, cur)});
|
543 | cur.changed = null;
|
544 | }
|
545 | }
|
546 |
|
547 | return {query: query, files: files};
|
548 | }
|
549 |
|
550 | function getFragmentAround(data, start, end) {
|
551 | var doc = data.doc;
|
552 | var minIndent = null, minLine = null, endLine, tabSize = 4;
|
553 | for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {
|
554 | var line = doc.getLine(p), fn = line.search(/\bfunction\b/);
|
555 | if (fn < 0) continue;
|
556 | var indent = CodeMirror.countColumn(line, null, tabSize);
|
557 | if (minIndent != null && minIndent <= indent) continue;
|
558 | minIndent = indent;
|
559 | minLine = p;
|
560 | }
|
561 | if (minLine == null) minLine = min;
|
562 | var max = Math.min(doc.lastLine(), end.line + 20);
|
563 | if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))
|
564 | endLine = max;
|
565 | else for (endLine = end.line + 1; endLine < max; ++endLine) {
|
566 | var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);
|
567 | if (indent <= minIndent) break;
|
568 | }
|
569 | var from = Pos(minLine, 0);
|
570 |
|
571 | return {type: "part",
|
572 | name: data.name,
|
573 | offsetLines: from.line,
|
574 | text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0))};
|
575 | }
|
576 |
|
577 |
|
578 |
|
579 | var cmpPos = CodeMirror.cmpPos;
|
580 |
|
581 | function elt(tagname, cls /*, ... elts*/) {
|
582 | var e = document.createElement(tagname);
|
583 | if (cls) e.className = cls;
|
584 | for (var i = 2; i < arguments.length; ++i) {
|
585 | var elt = arguments[i];
|
586 | if (typeof elt == "string") elt = document.createTextNode(elt);
|
587 | e.appendChild(elt);
|
588 | }
|
589 | return e;
|
590 | }
|
591 |
|
592 | function dialog(cm, text, f) {
|
593 | if (cm.openDialog)
|
594 | cm.openDialog(text + ": <input type=text>", f);
|
595 | else
|
596 | f(prompt(text, ""));
|
597 | }
|
598 |
|
599 |
|
600 |
|
601 | function tempTooltip(cm, content, ts) {
|
602 | if (cm.state.ternTooltip) remove(cm.state.ternTooltip);
|
603 | var where = cm.cursorCoords();
|
604 | var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content, cm);
|
605 | function maybeClear() {
|
606 | old = true;
|
607 | if (!mouseOnTip) clear();
|
608 | }
|
609 | function clear() {
|
610 | cm.state.ternTooltip = null;
|
611 | if (tip.parentNode) fadeOut(tip)
|
612 | clearActivity()
|
613 | }
|
614 | var mouseOnTip = false, old = false;
|
615 | CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
|
616 | CodeMirror.on(tip, "mouseout", function(e) {
|
617 | var related = e.relatedTarget || e.toElement
|
618 | if (!related || !CodeMirror.contains(tip, related)) {
|
619 | if (old) clear();
|
620 | else mouseOnTip = false;
|
621 | }
|
622 | });
|
623 | setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700);
|
624 | var clearActivity = onEditorActivity(cm, clear)
|
625 | }
|
626 |
|
627 | function onEditorActivity(cm, f) {
|
628 | cm.on("cursorActivity", f)
|
629 | cm.on("blur", f)
|
630 | cm.on("scroll", f)
|
631 | cm.on("setDoc", f)
|
632 | return function() {
|
633 | cm.off("cursorActivity", f)
|
634 | cm.off("blur", f)
|
635 | cm.off("scroll", f)
|
636 | cm.off("setDoc", f)
|
637 | }
|
638 | }
|
639 |
|
640 | function makeTooltip(x, y, content, cm) {
|
641 | var node = elt("div", cls + "tooltip", content);
|
642 | node.style.left = x + "px";
|
643 | node.style.top = y + "px";
|
644 | var container = ((cm.options || {}).hintOptions || {}).container || document.body;
|
645 | container.appendChild(node);
|
646 | return node;
|
647 | }
|
648 |
|
649 | function remove(node) {
|
650 | var p = node && node.parentNode;
|
651 | if (p) p.removeChild(node);
|
652 | }
|
653 |
|
654 | function fadeOut(tooltip) {
|
655 | tooltip.style.opacity = "0";
|
656 | setTimeout(function() { remove(tooltip); }, 1100);
|
657 | }
|
658 |
|
659 | function showError(ts, cm, msg) {
|
660 | if (ts.options.showError)
|
661 | ts.options.showError(cm, msg);
|
662 | else
|
663 | tempTooltip(cm, String(msg), ts);
|
664 | }
|
665 |
|
666 | function closeArgHints(ts) {
|
667 | if (ts.activeArgHints) {
|
668 | if (ts.activeArgHints.clear) ts.activeArgHints.clear()
|
669 | remove(ts.activeArgHints)
|
670 | ts.activeArgHints = null
|
671 | }
|
672 | }
|
673 |
|
674 | function docValue(ts, doc) {
|
675 | var val = doc.doc.getValue();
|
676 | if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);
|
677 | return val;
|
678 | }
|
679 |
|
680 |
|
681 |
|
682 | function WorkerServer(ts) {
|
683 | var worker = ts.worker = new Worker(ts.options.workerScript);
|
684 | worker.postMessage({type: "init",
|
685 | defs: ts.options.defs,
|
686 | plugins: ts.options.plugins,
|
687 | scripts: ts.options.workerDeps});
|
688 | var msgId = 0, pending = {};
|
689 |
|
690 | function send(data, c) {
|
691 | if (c) {
|
692 | data.id = ++msgId;
|
693 | pending[msgId] = c;
|
694 | }
|
695 | worker.postMessage(data);
|
696 | }
|
697 | worker.onmessage = function(e) {
|
698 | var data = e.data;
|
699 | if (data.type == "getFile") {
|
700 | getFile(ts, data.name, function(err, text) {
|
701 | send({type: "getFile", err: String(err), text: text, id: data.id});
|
702 | });
|
703 | } else if (data.type == "debug") {
|
704 | window.console.log(data.message);
|
705 | } else if (data.id && pending[data.id]) {
|
706 | pending[data.id](data.err, data.body);
|
707 | delete pending[data.id];
|
708 | }
|
709 | };
|
710 | worker.onerror = function(e) {
|
711 | for (var id in pending) pending[id](e);
|
712 | pending = {};
|
713 | };
|
714 |
|
715 | this.addFile = function(name, text) { send({type: "add", name: name, text: text}); };
|
716 | this.delFile = function(name) { send({type: "del", name: name}); };
|
717 | this.request = function(body, c) { send({type: "req", body: body}, c); };
|
718 | }
|
719 | });
|