spoint 0.1.0 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -209
- package/SKILL.md +95 -0
- package/apps/environment/index.js +200 -1
- package/apps/environment/models/decorative/.gitkeep +0 -0
- package/apps/environment/models/hazards/.gitkeep +0 -0
- package/apps/environment/models/interactive/.gitkeep +0 -0
- package/apps/environment/models/structures/.gitkeep +0 -0
- package/apps/environment/smartObjects.js +114 -0
- package/apps/interactable/index.js +155 -0
- package/apps/physics-crate/index.js +15 -9
- package/apps/power-crate/index.js +18 -12
- package/apps/tps-game/$GDUPI.vrm +0 -0
- package/apps/tps-game/Cleetus.vrm +0 -0
- package/apps/tps-game/index.js +185 -27
- package/apps/world/index.js +68 -22
- package/bin/create-app.js +337 -0
- package/client/ARControls.js +301 -0
- package/client/LoadingManager.js +117 -0
- package/client/MobileControls.js +1122 -0
- package/client/anim-lib.glb +0 -0
- package/client/animation.js +306 -0
- package/client/app.js +1341 -65
- package/client/camera.js +191 -33
- package/client/createLoadingScreen.js +69 -0
- package/client/editor/bridge.js +113 -0
- package/client/editor/css/main.css +794 -0
- package/client/editor/images/rotate.svg +4 -0
- package/client/editor/images/scale.svg +4 -0
- package/client/editor/images/translate.svg +4 -0
- package/client/editor/index.html +103 -0
- package/client/editor/js/Command.js +41 -0
- package/client/editor/js/Config.js +81 -0
- package/client/editor/js/Editor.js +785 -0
- package/client/editor/js/EditorControls.js +438 -0
- package/client/editor/js/History.js +321 -0
- package/client/editor/js/Loader.js +987 -0
- package/client/editor/js/LoaderUtils.js +90 -0
- package/client/editor/js/Menubar.Add.js +510 -0
- package/client/editor/js/Menubar.Edit.js +145 -0
- package/client/editor/js/Menubar.File.js +466 -0
- package/client/editor/js/Menubar.Help.js +73 -0
- package/client/editor/js/Menubar.Status.js +51 -0
- package/client/editor/js/Menubar.View.js +183 -0
- package/client/editor/js/Menubar.js +27 -0
- package/client/editor/js/Player.js +53 -0
- package/client/editor/js/Resizer.js +58 -0
- package/client/editor/js/Script.js +503 -0
- package/client/editor/js/Selector.js +102 -0
- package/client/editor/js/Sidebar.Geometry.BoxGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.BufferGeometry.js +115 -0
- package/client/editor/js/Sidebar.Geometry.CapsuleGeometry.js +97 -0
- package/client/editor/js/Sidebar.Geometry.CircleGeometry.js +97 -0
- package/client/editor/js/Sidebar.Geometry.CylinderGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.DodecahedronGeometry.js +73 -0
- package/client/editor/js/Sidebar.Geometry.ExtrudeGeometry.js +196 -0
- package/client/editor/js/Sidebar.Geometry.IcosahedronGeometry.js +73 -0
- package/client/editor/js/Sidebar.Geometry.LatheGeometry.js +98 -0
- package/client/editor/js/Sidebar.Geometry.Modifiers.js +73 -0
- package/client/editor/js/Sidebar.Geometry.OctahedronGeometry.js +74 -0
- package/client/editor/js/Sidebar.Geometry.PlaneGeometry.js +97 -0
- package/client/editor/js/Sidebar.Geometry.RingGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.ShapeGeometry.js +76 -0
- package/client/editor/js/Sidebar.Geometry.SphereGeometry.js +133 -0
- package/client/editor/js/Sidebar.Geometry.TetrahedronGeometry.js +74 -0
- package/client/editor/js/Sidebar.Geometry.TorusGeometry.js +109 -0
- package/client/editor/js/Sidebar.Geometry.TorusKnotGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.TubeGeometry.js +135 -0
- package/client/editor/js/Sidebar.Geometry.js +332 -0
- package/client/editor/js/Sidebar.Material.BooleanProperty.js +60 -0
- package/client/editor/js/Sidebar.Material.ColorProperty.js +87 -0
- package/client/editor/js/Sidebar.Material.ConstantProperty.js +62 -0
- package/client/editor/js/Sidebar.Material.MapProperty.js +249 -0
- package/client/editor/js/Sidebar.Material.NumberProperty.js +60 -0
- package/client/editor/js/Sidebar.Material.Program.js +73 -0
- package/client/editor/js/Sidebar.Material.RangeValueProperty.js +63 -0
- package/client/editor/js/Sidebar.Material.js +751 -0
- package/client/editor/js/Sidebar.Object.Animation.js +102 -0
- package/client/editor/js/Sidebar.Object.js +898 -0
- package/client/editor/js/Sidebar.Project.App.js +165 -0
- package/client/editor/js/Sidebar.Project.Image.js +225 -0
- package/client/editor/js/Sidebar.Project.Materials.js +82 -0
- package/client/editor/js/Sidebar.Project.Renderer.js +144 -0
- package/client/editor/js/Sidebar.Project.Video.js +242 -0
- package/client/editor/js/Sidebar.Project.js +31 -0
- package/client/editor/js/Sidebar.Properties.js +73 -0
- package/client/editor/js/Sidebar.Scene.js +585 -0
- package/client/editor/js/Sidebar.Script.js +129 -0
- package/client/editor/js/Sidebar.Settings.History.js +146 -0
- package/client/editor/js/Sidebar.Settings.Shortcuts.js +175 -0
- package/client/editor/js/Sidebar.Settings.js +60 -0
- package/client/editor/js/Sidebar.js +41 -0
- package/client/editor/js/Storage.js +98 -0
- package/client/editor/js/Strings.js +2028 -0
- package/client/editor/js/Toolbar.js +84 -0
- package/client/editor/js/Viewport.Controls.js +92 -0
- package/client/editor/js/Viewport.Info.js +136 -0
- package/client/editor/js/Viewport.Pathtracer.js +91 -0
- package/client/editor/js/Viewport.ViewHelper.js +39 -0
- package/client/editor/js/Viewport.XR.js +222 -0
- package/client/editor/js/Viewport.js +900 -0
- package/client/editor/js/commands/AddObjectCommand.js +68 -0
- package/client/editor/js/commands/AddScriptCommand.js +75 -0
- package/client/editor/js/commands/Commands.js +23 -0
- package/client/editor/js/commands/MoveObjectCommand.js +111 -0
- package/client/editor/js/commands/MultiCmdsCommand.js +85 -0
- package/client/editor/js/commands/RemoveObjectCommand.js +88 -0
- package/client/editor/js/commands/RemoveScriptCommand.js +81 -0
- package/client/editor/js/commands/SetColorCommand.js +73 -0
- package/client/editor/js/commands/SetGeometryCommand.js +87 -0
- package/client/editor/js/commands/SetGeometryValueCommand.js +70 -0
- package/client/editor/js/commands/SetMaterialColorCommand.js +86 -0
- package/client/editor/js/commands/SetMaterialCommand.js +79 -0
- package/client/editor/js/commands/SetMaterialMapCommand.js +143 -0
- package/client/editor/js/commands/SetMaterialRangeCommand.js +91 -0
- package/client/editor/js/commands/SetMaterialValueCommand.js +90 -0
- package/client/editor/js/commands/SetMaterialVectorCommand.js +79 -0
- package/client/editor/js/commands/SetPositionCommand.js +84 -0
- package/client/editor/js/commands/SetRotationCommand.js +84 -0
- package/client/editor/js/commands/SetScaleCommand.js +84 -0
- package/client/editor/js/commands/SetSceneCommand.js +103 -0
- package/client/editor/js/commands/SetScriptValueCommand.js +80 -0
- package/client/editor/js/commands/SetShadowValueCommand.js +73 -0
- package/client/editor/js/commands/SetUuidCommand.js +70 -0
- package/client/editor/js/commands/SetValueCommand.js +75 -0
- package/client/editor/js/libs/acorn/acorn.js +3236 -0
- package/client/editor/js/libs/acorn/acorn_loose.js +1299 -0
- package/client/editor/js/libs/acorn/walk.js +344 -0
- package/client/editor/js/libs/app/index.html +57 -0
- package/client/editor/js/libs/app.js +251 -0
- package/client/editor/js/libs/codemirror/addon/dialog.css +32 -0
- package/client/editor/js/libs/codemirror/addon/dialog.js +163 -0
- package/client/editor/js/libs/codemirror/addon/show-hint.css +36 -0
- package/client/editor/js/libs/codemirror/addon/show-hint.js +529 -0
- package/client/editor/js/libs/codemirror/addon/tern.css +87 -0
- package/client/editor/js/libs/codemirror/addon/tern.js +750 -0
- package/client/editor/js/libs/codemirror/codemirror.css +344 -0
- package/client/editor/js/libs/codemirror/codemirror.js +9849 -0
- package/client/editor/js/libs/codemirror/mode/glsl.js +233 -0
- package/client/editor/js/libs/codemirror/mode/javascript.js +959 -0
- package/client/editor/js/libs/codemirror/theme/monokai.css +41 -0
- package/client/editor/js/libs/esprima.js +6401 -0
- package/client/editor/js/libs/jsonlint.js +453 -0
- package/client/editor/js/libs/signals.min.js +14 -0
- package/client/editor/js/libs/tern-threejs/threejs.js +5031 -0
- package/client/editor/js/libs/ternjs/comment.js +87 -0
- package/client/editor/js/libs/ternjs/def.js +588 -0
- package/client/editor/js/libs/ternjs/doc_comment.js +401 -0
- package/client/editor/js/libs/ternjs/infer.js +1635 -0
- package/client/editor/js/libs/ternjs/polyfill.js +80 -0
- package/client/editor/js/libs/ternjs/signal.js +26 -0
- package/client/editor/js/libs/ternjs/tern.js +993 -0
- package/client/editor/js/libs/ui.js +1346 -0
- package/client/editor/js/libs/ui.three.js +855 -0
- package/client/facial-animation.js +455 -0
- package/client/index.html +7 -4
- package/client/loading.css +147 -0
- package/client/loading.html +25 -0
- package/client/style.css +251 -0
- package/package.json +7 -3
- package/server.js +9 -1
- package/src/apps/AppContext.js +1 -1
- package/src/apps/AppLoader.js +50 -37
- package/src/apps/AppRuntime.js +32 -8
- package/src/client/InputHandler.js +233 -0
- package/src/client/JitterBuffer.js +207 -0
- package/src/client/KalmanFilter.js +125 -0
- package/src/client/MessageHandler.js +101 -0
- package/src/client/PhysicsNetworkClient.js +141 -68
- package/src/client/ReconnectManager.js +62 -0
- package/src/client/SmoothInterpolation.js +127 -0
- package/src/client/SnapshotProcessor.js +144 -0
- package/src/connection/ConnectionManager.js +21 -3
- package/src/connection/SessionStore.js +13 -3
- package/src/index.client.js +4 -6
- package/src/netcode/EventLog.js +29 -15
- package/src/netcode/LagCompensator.js +25 -26
- package/src/netcode/NetworkState.js +4 -1
- package/src/netcode/PhysicsIntegration.js +20 -6
- package/src/netcode/PlayerManager.js +10 -2
- package/src/netcode/SnapshotEncoder.js +66 -19
- package/src/netcode/TickSystem.js +13 -4
- package/src/physics/World.js +66 -13
- package/src/protocol/msgpack.js +90 -63
- package/src/sdk/ReloadHandlers.js +12 -2
- package/src/sdk/ReloadManager.js +5 -0
- package/src/sdk/ServerHandlers.js +50 -11
- package/src/sdk/StaticHandler.js +22 -6
- package/src/sdk/TickHandler.js +101 -34
- package/src/sdk/scaffold.js +31 -0
- package/src/sdk/server.js +59 -33
- package/src/shared/movement.js +2 -1
- package/src/spatial/Octree.js +5 -0
- package/apps/interactive-door/index.js +0 -33
- package/apps/patrol-npc/index.js +0 -37
- package/src/connection/QualityMonitor.js +0 -46
- package/src/debug/StateInspector.js +0 -42
- package/src/index.js +0 -1
- package/src/index.server.js +0 -27
- package/src/protocol/Codec.js +0 -60
- package/src/protocol/SequenceTracker.js +0 -71
- package/src/sdk/ClientMessageHandler.js +0 -80
- package/src/sdk/client.js +0 -122
- package/world/kaira.glb +0 -0
- package/world/schwust.glb +0 -0
|
@@ -0,0 +1,993 @@
|
|
|
1
|
+
// The Tern server object
|
|
2
|
+
|
|
3
|
+
// A server is a stateful object that manages the analysis for a
|
|
4
|
+
// project, and defines an interface for querying the code in the
|
|
5
|
+
// project.
|
|
6
|
+
|
|
7
|
+
(function(root, mod) {
|
|
8
|
+
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
9
|
+
return mod(exports, require("./infer"), require("./signal"),
|
|
10
|
+
require("acorn"), require("acorn/dist/walk"));
|
|
11
|
+
if (typeof define == "function" && define.amd) // AMD
|
|
12
|
+
return define(["exports", "./infer", "./signal", "acorn/dist/acorn", "acorn/dist/walk"], mod);
|
|
13
|
+
mod(root.tern || (root.tern = {}), tern, tern.signal, acorn, acorn.walk); // Plain browser env
|
|
14
|
+
})(this, function(exports, infer, signal, acorn, walk) {
|
|
15
|
+
"use strict";
|
|
16
|
+
|
|
17
|
+
var plugins = Object.create(null);
|
|
18
|
+
exports.registerPlugin = function(name, init) { plugins[name] = init; };
|
|
19
|
+
|
|
20
|
+
var defaultOptions = exports.defaultOptions = {
|
|
21
|
+
debug: false,
|
|
22
|
+
async: false,
|
|
23
|
+
getFile: function(_f, c) { if (this.async) c(null, null); },
|
|
24
|
+
defs: [],
|
|
25
|
+
plugins: {},
|
|
26
|
+
fetchTimeout: 1000,
|
|
27
|
+
dependencyBudget: 20000,
|
|
28
|
+
reuseInstances: true,
|
|
29
|
+
stripCRs: false
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
var queryTypes = {
|
|
33
|
+
completions: {
|
|
34
|
+
takesFile: true,
|
|
35
|
+
run: findCompletions
|
|
36
|
+
},
|
|
37
|
+
properties: {
|
|
38
|
+
run: findProperties
|
|
39
|
+
},
|
|
40
|
+
type: {
|
|
41
|
+
takesFile: true,
|
|
42
|
+
run: findTypeAt
|
|
43
|
+
},
|
|
44
|
+
documentation: {
|
|
45
|
+
takesFile: true,
|
|
46
|
+
run: findDocs
|
|
47
|
+
},
|
|
48
|
+
definition: {
|
|
49
|
+
takesFile: true,
|
|
50
|
+
run: findDef
|
|
51
|
+
},
|
|
52
|
+
refs: {
|
|
53
|
+
takesFile: true,
|
|
54
|
+
fullFile: true,
|
|
55
|
+
run: findRefs
|
|
56
|
+
},
|
|
57
|
+
rename: {
|
|
58
|
+
takesFile: true,
|
|
59
|
+
fullFile: true,
|
|
60
|
+
run: buildRename
|
|
61
|
+
},
|
|
62
|
+
files: {
|
|
63
|
+
run: listFiles
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
exports.defineQueryType = function(name, desc) { queryTypes[name] = desc; };
|
|
68
|
+
|
|
69
|
+
function File(name, parent) {
|
|
70
|
+
this.name = name;
|
|
71
|
+
this.parent = parent;
|
|
72
|
+
this.scope = this.text = this.ast = this.lineOffsets = null;
|
|
73
|
+
}
|
|
74
|
+
File.prototype.asLineChar = function(pos) { return asLineChar(this, pos); };
|
|
75
|
+
|
|
76
|
+
function updateText(file, text, srv) {
|
|
77
|
+
file.text = srv.options.stripCRs ? text.replace(/\r\n/g, "\n") : text;
|
|
78
|
+
infer.withContext(srv.cx, function() {
|
|
79
|
+
file.ast = infer.parse(file.text, srv.passes, {directSourceFile: file, allowReturnOutsideFunction: true});
|
|
80
|
+
});
|
|
81
|
+
file.lineOffsets = null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
var Server = exports.Server = function(options) {
|
|
85
|
+
this.cx = null;
|
|
86
|
+
this.options = options || {};
|
|
87
|
+
for (var o in defaultOptions) if (!options.hasOwnProperty(o))
|
|
88
|
+
options[o] = defaultOptions[o];
|
|
89
|
+
|
|
90
|
+
this.handlers = Object.create(null);
|
|
91
|
+
this.files = [];
|
|
92
|
+
this.fileMap = Object.create(null);
|
|
93
|
+
this.needsPurge = [];
|
|
94
|
+
this.budgets = Object.create(null);
|
|
95
|
+
this.uses = 0;
|
|
96
|
+
this.pending = 0;
|
|
97
|
+
this.asyncError = null;
|
|
98
|
+
this.passes = Object.create(null);
|
|
99
|
+
|
|
100
|
+
this.defs = options.defs.slice(0);
|
|
101
|
+
for (var plugin in options.plugins) if (options.plugins.hasOwnProperty(plugin) && plugin in plugins) {
|
|
102
|
+
var init = plugins[plugin](this, options.plugins[plugin]);
|
|
103
|
+
if (init && init.defs) {
|
|
104
|
+
if (init.loadFirst) this.defs.unshift(init.defs);
|
|
105
|
+
else this.defs.push(init.defs);
|
|
106
|
+
}
|
|
107
|
+
if (init && init.passes) for (var type in init.passes) if (init.passes.hasOwnProperty(type))
|
|
108
|
+
(this.passes[type] || (this.passes[type] = [])).push(init.passes[type]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.reset();
|
|
112
|
+
};
|
|
113
|
+
Server.prototype = signal.mixin({
|
|
114
|
+
addFile: function(name, /*optional*/ text, parent) {
|
|
115
|
+
// Don't crash when sloppy plugins pass non-existent parent ids
|
|
116
|
+
if (parent && !(parent in this.fileMap)) parent = null;
|
|
117
|
+
ensureFile(this, name, parent, text);
|
|
118
|
+
},
|
|
119
|
+
delFile: function(name) {
|
|
120
|
+
var file = this.findFile(name);
|
|
121
|
+
if (file) {
|
|
122
|
+
this.needsPurge.push(file.name);
|
|
123
|
+
this.files.splice(this.files.indexOf(file), 1);
|
|
124
|
+
delete this.fileMap[name];
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
reset: function() {
|
|
128
|
+
this.signal("reset");
|
|
129
|
+
this.cx = new infer.Context(this.defs, this);
|
|
130
|
+
this.uses = 0;
|
|
131
|
+
this.budgets = Object.create(null);
|
|
132
|
+
for (var i = 0; i < this.files.length; ++i) {
|
|
133
|
+
var file = this.files[i];
|
|
134
|
+
file.scope = null;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
request: function(doc, c) {
|
|
139
|
+
var inv = invalidDoc(doc);
|
|
140
|
+
if (inv) return c(inv);
|
|
141
|
+
|
|
142
|
+
var self = this;
|
|
143
|
+
doRequest(this, doc, function(err, data) {
|
|
144
|
+
c(err, data);
|
|
145
|
+
if (self.uses > 40) {
|
|
146
|
+
self.reset();
|
|
147
|
+
analyzeAll(self, null, function(){});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
findFile: function(name) {
|
|
153
|
+
return this.fileMap[name];
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
flush: function(c) {
|
|
157
|
+
var cx = this.cx;
|
|
158
|
+
analyzeAll(this, null, function(err) {
|
|
159
|
+
if (err) return c(err);
|
|
160
|
+
infer.withContext(cx, c);
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
startAsyncAction: function() {
|
|
165
|
+
++this.pending;
|
|
166
|
+
},
|
|
167
|
+
finishAsyncAction: function(err) {
|
|
168
|
+
if (err) this.asyncError = err;
|
|
169
|
+
if (--this.pending === 0) this.signal("everythingFetched");
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
function doRequest(srv, doc, c) {
|
|
174
|
+
if (doc.query && !queryTypes.hasOwnProperty(doc.query.type))
|
|
175
|
+
return c("No query type '" + doc.query.type + "' defined");
|
|
176
|
+
|
|
177
|
+
var query = doc.query;
|
|
178
|
+
// Respond as soon as possible when this just uploads files
|
|
179
|
+
if (!query) c(null, {});
|
|
180
|
+
|
|
181
|
+
var files = doc.files || [];
|
|
182
|
+
if (files.length) ++srv.uses;
|
|
183
|
+
for (var i = 0; i < files.length; ++i) {
|
|
184
|
+
var file = files[i];
|
|
185
|
+
if (file.type == "delete")
|
|
186
|
+
srv.delFile(file.name);
|
|
187
|
+
else
|
|
188
|
+
ensureFile(srv, file.name, null, file.type == "full" ? file.text : null);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
var timeBudget = typeof doc.timeout == "number" ? [doc.timeout] : null;
|
|
192
|
+
if (!query) {
|
|
193
|
+
analyzeAll(srv, timeBudget, function(){});
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
var queryType = queryTypes[query.type];
|
|
198
|
+
if (queryType.takesFile) {
|
|
199
|
+
if (typeof query.file != "string") return c(".query.file must be a string");
|
|
200
|
+
if (!/^#/.test(query.file)) ensureFile(srv, query.file, null);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
analyzeAll(srv, timeBudget, function(err) {
|
|
204
|
+
if (err) return c(err);
|
|
205
|
+
var file = queryType.takesFile && resolveFile(srv, files, query.file);
|
|
206
|
+
if (queryType.fullFile && file.type == "part")
|
|
207
|
+
return c("Can't run a " + query.type + " query on a file fragment");
|
|
208
|
+
|
|
209
|
+
function run() {
|
|
210
|
+
var result;
|
|
211
|
+
try {
|
|
212
|
+
result = queryType.run(srv, query, file);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
if (srv.options.debug && e.name != "TernError") console.error(e.stack);
|
|
215
|
+
return c(e);
|
|
216
|
+
}
|
|
217
|
+
c(null, result);
|
|
218
|
+
}
|
|
219
|
+
infer.withContext(srv.cx, timeBudget ? function() { infer.withTimeout(timeBudget[0], run); } : run);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function analyzeFile(srv, file) {
|
|
224
|
+
infer.withContext(srv.cx, function() {
|
|
225
|
+
file.scope = srv.cx.topScope;
|
|
226
|
+
srv.signal("beforeLoad", file);
|
|
227
|
+
infer.analyze(file.ast, file.name, file.scope, srv.passes);
|
|
228
|
+
srv.signal("afterLoad", file);
|
|
229
|
+
});
|
|
230
|
+
return file;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function ensureFile(srv, name, parent, text) {
|
|
234
|
+
var known = srv.findFile(name);
|
|
235
|
+
if (known) {
|
|
236
|
+
if (text != null) {
|
|
237
|
+
if (known.scope) {
|
|
238
|
+
srv.needsPurge.push(name);
|
|
239
|
+
known.scope = null;
|
|
240
|
+
}
|
|
241
|
+
updateText(known, text, srv);
|
|
242
|
+
}
|
|
243
|
+
if (parentDepth(srv, known.parent) > parentDepth(srv, parent)) {
|
|
244
|
+
known.parent = parent;
|
|
245
|
+
if (known.excluded) known.excluded = null;
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
var file = new File(name, parent);
|
|
251
|
+
srv.files.push(file);
|
|
252
|
+
srv.fileMap[name] = file;
|
|
253
|
+
if (text != null) {
|
|
254
|
+
updateText(file, text, srv);
|
|
255
|
+
} else if (srv.options.async) {
|
|
256
|
+
srv.startAsyncAction();
|
|
257
|
+
srv.options.getFile(name, function(err, text) {
|
|
258
|
+
updateText(file, text || "", srv);
|
|
259
|
+
srv.finishAsyncAction(err);
|
|
260
|
+
});
|
|
261
|
+
} else {
|
|
262
|
+
updateText(file, srv.options.getFile(name) || "", srv);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function fetchAll(srv, c) {
|
|
267
|
+
var done = true, returned = false;
|
|
268
|
+
srv.files.forEach(function(file) {
|
|
269
|
+
if (file.text != null) return;
|
|
270
|
+
if (srv.options.async) {
|
|
271
|
+
done = false;
|
|
272
|
+
srv.options.getFile(file.name, function(err, text) {
|
|
273
|
+
if (err && !returned) { returned = true; return c(err); }
|
|
274
|
+
updateText(file, text || "", srv);
|
|
275
|
+
fetchAll(srv, c);
|
|
276
|
+
});
|
|
277
|
+
} else {
|
|
278
|
+
try {
|
|
279
|
+
updateText(file, srv.options.getFile(file.name) || "", srv);
|
|
280
|
+
} catch (e) { return c(e); }
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
if (done) c();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function waitOnFetch(srv, timeBudget, c) {
|
|
287
|
+
var done = function() {
|
|
288
|
+
srv.off("everythingFetched", done);
|
|
289
|
+
clearTimeout(timeout);
|
|
290
|
+
analyzeAll(srv, timeBudget, c);
|
|
291
|
+
};
|
|
292
|
+
srv.on("everythingFetched", done);
|
|
293
|
+
var timeout = setTimeout(done, srv.options.fetchTimeout);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function analyzeAll(srv, timeBudget, c) {
|
|
297
|
+
if (srv.pending) return waitOnFetch(srv, timeBudget, c);
|
|
298
|
+
|
|
299
|
+
var e = srv.fetchError;
|
|
300
|
+
if (e) { srv.fetchError = null; return c(e); }
|
|
301
|
+
|
|
302
|
+
if (srv.needsPurge.length > 0) infer.withContext(srv.cx, function() {
|
|
303
|
+
infer.purge(srv.needsPurge);
|
|
304
|
+
srv.needsPurge.length = 0;
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
var done = true;
|
|
308
|
+
// The second inner loop might add new files. The outer loop keeps
|
|
309
|
+
// repeating both inner loops until all files have been looked at.
|
|
310
|
+
for (var i = 0; i < srv.files.length;) {
|
|
311
|
+
var toAnalyze = [];
|
|
312
|
+
for (; i < srv.files.length; ++i) {
|
|
313
|
+
var file = srv.files[i];
|
|
314
|
+
if (file.text == null) done = false;
|
|
315
|
+
else if (file.scope == null && !file.excluded) toAnalyze.push(file);
|
|
316
|
+
}
|
|
317
|
+
toAnalyze.sort(function(a, b) {
|
|
318
|
+
return parentDepth(srv, a.parent) - parentDepth(srv, b.parent);
|
|
319
|
+
});
|
|
320
|
+
for (var j = 0; j < toAnalyze.length; j++) {
|
|
321
|
+
var file = toAnalyze[j];
|
|
322
|
+
if (file.parent && !chargeOnBudget(srv, file)) {
|
|
323
|
+
file.excluded = true;
|
|
324
|
+
} else if (timeBudget) {
|
|
325
|
+
var startTime = +new Date;
|
|
326
|
+
infer.withTimeout(timeBudget[0], function() { analyzeFile(srv, file); });
|
|
327
|
+
timeBudget[0] -= +new Date - startTime;
|
|
328
|
+
} else {
|
|
329
|
+
analyzeFile(srv, file);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (done) c();
|
|
334
|
+
else waitOnFetch(srv, timeBudget, c);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function firstLine(str) {
|
|
338
|
+
var end = str.indexOf("\n");
|
|
339
|
+
if (end < 0) return str;
|
|
340
|
+
return str.slice(0, end);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function findMatchingPosition(line, file, near) {
|
|
344
|
+
var pos = Math.max(0, near - 500), closest = null;
|
|
345
|
+
if (!/^\s*$/.test(line)) for (;;) {
|
|
346
|
+
var found = file.indexOf(line, pos);
|
|
347
|
+
if (found < 0 || found > near + 500) break;
|
|
348
|
+
if (closest == null || Math.abs(closest - near) > Math.abs(found - near))
|
|
349
|
+
closest = found;
|
|
350
|
+
pos = found + line.length;
|
|
351
|
+
}
|
|
352
|
+
return closest;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function scopeDepth(s) {
|
|
356
|
+
for (var i = 0; s; ++i, s = s.prev) {}
|
|
357
|
+
return i;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function ternError(msg) {
|
|
361
|
+
var err = new Error(msg);
|
|
362
|
+
err.name = "TernError";
|
|
363
|
+
return err;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function resolveFile(srv, localFiles, name) {
|
|
367
|
+
var isRef = name.match(/^#(\d+)$/);
|
|
368
|
+
if (!isRef) return srv.findFile(name);
|
|
369
|
+
|
|
370
|
+
var file = localFiles[isRef[1]];
|
|
371
|
+
if (!file || file.type == "delete") throw ternError("Reference to unknown file " + name);
|
|
372
|
+
if (file.type == "full") return srv.findFile(file.name);
|
|
373
|
+
|
|
374
|
+
// This is a partial file
|
|
375
|
+
|
|
376
|
+
var realFile = file.backing = srv.findFile(file.name);
|
|
377
|
+
var offset;
|
|
378
|
+
file.offset = offset = resolvePos(realFile, file.offsetLines == null ? file.offset : {line: file.offsetLines, ch: 0}, true);
|
|
379
|
+
var line = firstLine(file.text);
|
|
380
|
+
var foundPos = findMatchingPosition(line, realFile.text, offset);
|
|
381
|
+
var pos = foundPos == null ? Math.max(0, realFile.text.lastIndexOf("\n", offset)) : foundPos;
|
|
382
|
+
var inObject, atFunction;
|
|
383
|
+
|
|
384
|
+
infer.withContext(srv.cx, function() {
|
|
385
|
+
infer.purge(file.name, pos, pos + file.text.length);
|
|
386
|
+
|
|
387
|
+
var text = file.text, m;
|
|
388
|
+
if (m = text.match(/(?:"([^"]*)"|([\w$]+))\s*:\s*function\b/)) {
|
|
389
|
+
var objNode = walk.findNodeAround(file.backing.ast, pos, "ObjectExpression");
|
|
390
|
+
if (objNode && objNode.node.objType)
|
|
391
|
+
inObject = {type: objNode.node.objType, prop: m[2] || m[1]};
|
|
392
|
+
}
|
|
393
|
+
if (foundPos && (m = line.match(/^(.*?)\bfunction\b/))) {
|
|
394
|
+
var cut = m[1].length, white = "";
|
|
395
|
+
for (var i = 0; i < cut; ++i) white += " ";
|
|
396
|
+
text = white + text.slice(cut);
|
|
397
|
+
atFunction = true;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
var scopeStart = infer.scopeAt(realFile.ast, pos, realFile.scope);
|
|
401
|
+
var scopeEnd = infer.scopeAt(realFile.ast, pos + text.length, realFile.scope);
|
|
402
|
+
var scope = file.scope = scopeDepth(scopeStart) < scopeDepth(scopeEnd) ? scopeEnd : scopeStart;
|
|
403
|
+
file.ast = infer.parse(text, srv.passes, {directSourceFile: file, allowReturnOutsideFunction: true});
|
|
404
|
+
infer.analyze(file.ast, file.name, scope, srv.passes);
|
|
405
|
+
|
|
406
|
+
// This is a kludge to tie together the function types (if any)
|
|
407
|
+
// outside and inside of the fragment, so that arguments and
|
|
408
|
+
// return values have some information known about them.
|
|
409
|
+
tieTogether: if (inObject || atFunction) {
|
|
410
|
+
var newInner = infer.scopeAt(file.ast, line.length, scopeStart);
|
|
411
|
+
if (!newInner.fnType) break tieTogether;
|
|
412
|
+
if (inObject) {
|
|
413
|
+
var prop = inObject.type.getProp(inObject.prop);
|
|
414
|
+
prop.addType(newInner.fnType);
|
|
415
|
+
} else if (atFunction) {
|
|
416
|
+
var inner = infer.scopeAt(realFile.ast, pos + line.length, realFile.scope);
|
|
417
|
+
if (inner == scopeStart || !inner.fnType) break tieTogether;
|
|
418
|
+
var fOld = inner.fnType, fNew = newInner.fnType;
|
|
419
|
+
if (!fNew || (fNew.name != fOld.name && fOld.name)) break tieTogether;
|
|
420
|
+
for (var i = 0, e = Math.min(fOld.args.length, fNew.args.length); i < e; ++i)
|
|
421
|
+
fOld.args[i].propagate(fNew.args[i]);
|
|
422
|
+
fOld.self.propagate(fNew.self);
|
|
423
|
+
fNew.retval.propagate(fOld.retval);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
return file;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Budget management
|
|
431
|
+
|
|
432
|
+
function astSize(node) {
|
|
433
|
+
var size = 0;
|
|
434
|
+
walk.simple(node, {Expression: function() { ++size; }});
|
|
435
|
+
return size;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function parentDepth(srv, parent) {
|
|
439
|
+
var depth = 0;
|
|
440
|
+
while (parent) {
|
|
441
|
+
parent = srv.findFile(parent).parent;
|
|
442
|
+
++depth;
|
|
443
|
+
}
|
|
444
|
+
return depth;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function budgetName(srv, file) {
|
|
448
|
+
for (;;) {
|
|
449
|
+
var parent = srv.findFile(file.parent);
|
|
450
|
+
if (!parent.parent) break;
|
|
451
|
+
file = parent;
|
|
452
|
+
}
|
|
453
|
+
return file.name;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function chargeOnBudget(srv, file) {
|
|
457
|
+
var bName = budgetName(srv, file);
|
|
458
|
+
var size = astSize(file.ast);
|
|
459
|
+
var known = srv.budgets[bName];
|
|
460
|
+
if (known == null)
|
|
461
|
+
known = srv.budgets[bName] = srv.options.dependencyBudget;
|
|
462
|
+
if (known < size) return false;
|
|
463
|
+
srv.budgets[bName] = known - size;
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Query helpers
|
|
468
|
+
|
|
469
|
+
function isPosition(val) {
|
|
470
|
+
return typeof val == "number" || typeof val == "object" &&
|
|
471
|
+
typeof val.line == "number" && typeof val.ch == "number";
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Baseline query document validation
|
|
475
|
+
function invalidDoc(doc) {
|
|
476
|
+
if (doc.query) {
|
|
477
|
+
if (typeof doc.query.type != "string") return ".query.type must be a string";
|
|
478
|
+
if (doc.query.start && !isPosition(doc.query.start)) return ".query.start must be a position";
|
|
479
|
+
if (doc.query.end && !isPosition(doc.query.end)) return ".query.end must be a position";
|
|
480
|
+
}
|
|
481
|
+
if (doc.files) {
|
|
482
|
+
if (!Array.isArray(doc.files)) return "Files property must be an array";
|
|
483
|
+
for (var i = 0; i < doc.files.length; ++i) {
|
|
484
|
+
var file = doc.files[i];
|
|
485
|
+
if (typeof file != "object") return ".files[n] must be objects";
|
|
486
|
+
else if (typeof file.name != "string") return ".files[n].name must be a string";
|
|
487
|
+
else if (file.type == "delete") continue;
|
|
488
|
+
else if (typeof file.text != "string") return ".files[n].text must be a string";
|
|
489
|
+
else if (file.type == "part") {
|
|
490
|
+
if (!isPosition(file.offset) && typeof file.offsetLines != "number")
|
|
491
|
+
return ".files[n].offset must be a position";
|
|
492
|
+
} else if (file.type != "full") return ".files[n].type must be \"full\" or \"part\"";
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
var offsetSkipLines = 25;
|
|
498
|
+
|
|
499
|
+
function findLineStart(file, line) {
|
|
500
|
+
var text = file.text, offsets = file.lineOffsets || (file.lineOffsets = [0]);
|
|
501
|
+
var pos = 0, curLine = 0;
|
|
502
|
+
var storePos = Math.min(Math.floor(line / offsetSkipLines), offsets.length - 1);
|
|
503
|
+
var pos = offsets[storePos], curLine = storePos * offsetSkipLines;
|
|
504
|
+
|
|
505
|
+
while (curLine < line) {
|
|
506
|
+
++curLine;
|
|
507
|
+
pos = text.indexOf("\n", pos) + 1;
|
|
508
|
+
if (pos === 0) return null;
|
|
509
|
+
if (curLine % offsetSkipLines === 0) offsets.push(pos);
|
|
510
|
+
}
|
|
511
|
+
return pos;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
var resolvePos = exports.resolvePos = function(file, pos, tolerant) {
|
|
515
|
+
if (typeof pos != "number") {
|
|
516
|
+
var lineStart = findLineStart(file, pos.line);
|
|
517
|
+
if (lineStart == null) {
|
|
518
|
+
if (tolerant) pos = file.text.length;
|
|
519
|
+
else throw ternError("File doesn't contain a line " + pos.line);
|
|
520
|
+
} else {
|
|
521
|
+
pos = lineStart + pos.ch;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (pos > file.text.length) {
|
|
525
|
+
if (tolerant) pos = file.text.length;
|
|
526
|
+
else throw ternError("Position " + pos + " is outside of file.");
|
|
527
|
+
}
|
|
528
|
+
return pos;
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
function asLineChar(file, pos) {
|
|
532
|
+
if (!file) return {line: 0, ch: 0};
|
|
533
|
+
var offsets = file.lineOffsets || (file.lineOffsets = [0]);
|
|
534
|
+
var text = file.text, line, lineStart;
|
|
535
|
+
for (var i = offsets.length - 1; i >= 0; --i) if (offsets[i] <= pos) {
|
|
536
|
+
line = i * offsetSkipLines;
|
|
537
|
+
lineStart = offsets[i];
|
|
538
|
+
}
|
|
539
|
+
for (;;) {
|
|
540
|
+
var eol = text.indexOf("\n", lineStart);
|
|
541
|
+
if (eol >= pos || eol < 0) break;
|
|
542
|
+
lineStart = eol + 1;
|
|
543
|
+
++line;
|
|
544
|
+
}
|
|
545
|
+
return {line: line, ch: pos - lineStart};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
var outputPos = exports.outputPos = function(query, file, pos) {
|
|
549
|
+
if (query.lineCharPositions) {
|
|
550
|
+
var out = asLineChar(file, pos);
|
|
551
|
+
if (file.type == "part")
|
|
552
|
+
out.line += file.offsetLines != null ? file.offsetLines : asLineChar(file.backing, file.offset).line;
|
|
553
|
+
return out;
|
|
554
|
+
} else {
|
|
555
|
+
return pos + (file.type == "part" ? file.offset : 0);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// Delete empty fields from result objects
|
|
560
|
+
function clean(obj) {
|
|
561
|
+
for (var prop in obj) if (obj[prop] == null) delete obj[prop];
|
|
562
|
+
return obj;
|
|
563
|
+
}
|
|
564
|
+
function maybeSet(obj, prop, val) {
|
|
565
|
+
if (val != null) obj[prop] = val;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Built-in query types
|
|
569
|
+
|
|
570
|
+
function compareCompletions(a, b) {
|
|
571
|
+
if (typeof a != "string") { a = a.name; b = b.name; }
|
|
572
|
+
var aUp = /^[A-Z]/.test(a), bUp = /^[A-Z]/.test(b);
|
|
573
|
+
if (aUp == bUp) return a < b ? -1 : a == b ? 0 : 1;
|
|
574
|
+
else return aUp ? 1 : -1;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function isStringAround(node, start, end) {
|
|
578
|
+
return node.type == "Literal" && typeof node.value == "string" &&
|
|
579
|
+
node.start == start - 1 && node.end <= end + 1;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function pointInProp(objNode, point) {
|
|
583
|
+
for (var i = 0; i < objNode.properties.length; i++) {
|
|
584
|
+
var curProp = objNode.properties[i];
|
|
585
|
+
if (curProp.key.start <= point && curProp.key.end >= point)
|
|
586
|
+
return curProp;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
var jsKeywords = ("break do instanceof typeof case else new var " +
|
|
591
|
+
"catch finally return void continue for switch while debugger " +
|
|
592
|
+
"function this with default if throw delete in try").split(" ");
|
|
593
|
+
|
|
594
|
+
function findCompletions(srv, query, file) {
|
|
595
|
+
if (query.end == null) throw ternError("missing .query.end field");
|
|
596
|
+
if (srv.passes.completion) for (var i = 0; i < srv.passes.completion.length; i++) {
|
|
597
|
+
var result = srv.passes.completion[i](file, query);
|
|
598
|
+
if (result) return result;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
var wordStart = resolvePos(file, query.end), wordEnd = wordStart, text = file.text;
|
|
602
|
+
while (wordStart && acorn.isIdentifierChar(text.charCodeAt(wordStart - 1))) --wordStart;
|
|
603
|
+
if (query.expandWordForward !== false)
|
|
604
|
+
while (wordEnd < text.length && acorn.isIdentifierChar(text.charCodeAt(wordEnd))) ++wordEnd;
|
|
605
|
+
var word = text.slice(wordStart, wordEnd), completions = [], ignoreObj;
|
|
606
|
+
if (query.caseInsensitive) word = word.toLowerCase();
|
|
607
|
+
var wrapAsObjs = query.types || query.depths || query.docs || query.urls || query.origins;
|
|
608
|
+
|
|
609
|
+
function gather(prop, obj, depth, addInfo) {
|
|
610
|
+
// 'hasOwnProperty' and such are usually just noise, leave them
|
|
611
|
+
// out when no prefix is provided.
|
|
612
|
+
if ((objLit || query.omitObjectPrototype !== false) && obj == srv.cx.protos.Object && !word) return;
|
|
613
|
+
if (query.filter !== false && word &&
|
|
614
|
+
(query.caseInsensitive ? prop.toLowerCase() : prop).indexOf(word) !== 0) return;
|
|
615
|
+
if (ignoreObj && ignoreObj.props[prop]) return;
|
|
616
|
+
for (var i = 0; i < completions.length; ++i) {
|
|
617
|
+
var c = completions[i];
|
|
618
|
+
if ((wrapAsObjs ? c.name : c) == prop) return;
|
|
619
|
+
}
|
|
620
|
+
var rec = wrapAsObjs ? {name: prop} : prop;
|
|
621
|
+
completions.push(rec);
|
|
622
|
+
|
|
623
|
+
if (obj && (query.types || query.docs || query.urls || query.origins)) {
|
|
624
|
+
var val = obj.props[prop];
|
|
625
|
+
infer.resetGuessing();
|
|
626
|
+
var type = val.getType();
|
|
627
|
+
rec.guess = infer.didGuess();
|
|
628
|
+
if (query.types)
|
|
629
|
+
rec.type = infer.toString(val);
|
|
630
|
+
if (query.docs)
|
|
631
|
+
maybeSet(rec, "doc", val.doc || type && type.doc);
|
|
632
|
+
if (query.urls)
|
|
633
|
+
maybeSet(rec, "url", val.url || type && type.url);
|
|
634
|
+
if (query.origins)
|
|
635
|
+
maybeSet(rec, "origin", val.origin || type && type.origin);
|
|
636
|
+
}
|
|
637
|
+
if (query.depths) rec.depth = depth;
|
|
638
|
+
if (wrapAsObjs && addInfo) addInfo(rec);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
var hookname, prop, objType, isKey;
|
|
642
|
+
|
|
643
|
+
var exprAt = infer.findExpressionAround(file.ast, null, wordStart, file.scope);
|
|
644
|
+
var memberExpr, objLit;
|
|
645
|
+
// Decide whether this is an object property, either in a member
|
|
646
|
+
// expression or an object literal.
|
|
647
|
+
if (exprAt) {
|
|
648
|
+
if (exprAt.node.type == "MemberExpression" && exprAt.node.object.end < wordStart) {
|
|
649
|
+
memberExpr = exprAt;
|
|
650
|
+
} else if (isStringAround(exprAt.node, wordStart, wordEnd)) {
|
|
651
|
+
var parent = infer.parentNode(exprAt.node, file.ast);
|
|
652
|
+
if (parent.type == "MemberExpression" && parent.property == exprAt.node)
|
|
653
|
+
memberExpr = {node: parent, state: exprAt.state};
|
|
654
|
+
} else if (exprAt.node.type == "ObjectExpression") {
|
|
655
|
+
var objProp = pointInProp(exprAt.node, wordEnd);
|
|
656
|
+
if (objProp) {
|
|
657
|
+
objLit = exprAt;
|
|
658
|
+
prop = isKey = objProp.key.name;
|
|
659
|
+
} else if (!word && !/:\s*$/.test(file.text.slice(0, wordStart))) {
|
|
660
|
+
objLit = exprAt;
|
|
661
|
+
prop = isKey = true;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (objLit) {
|
|
667
|
+
// Since we can't use the type of the literal itself to complete
|
|
668
|
+
// its properties (it doesn't contain the information we need),
|
|
669
|
+
// we have to try asking the surrounding expression for type info.
|
|
670
|
+
objType = infer.typeFromContext(file.ast, objLit);
|
|
671
|
+
ignoreObj = objLit.node.objType;
|
|
672
|
+
} else if (memberExpr) {
|
|
673
|
+
prop = memberExpr.node.property;
|
|
674
|
+
prop = prop.type == "Literal" ? prop.value.slice(1) : prop.name;
|
|
675
|
+
memberExpr.node = memberExpr.node.object;
|
|
676
|
+
objType = infer.expressionType(memberExpr);
|
|
677
|
+
} else if (text.charAt(wordStart - 1) == ".") {
|
|
678
|
+
var pathStart = wordStart - 1;
|
|
679
|
+
while (pathStart && (text.charAt(pathStart - 1) == "." || acorn.isIdentifierChar(text.charCodeAt(pathStart - 1)))) pathStart--;
|
|
680
|
+
var path = text.slice(pathStart, wordStart - 1);
|
|
681
|
+
if (path) {
|
|
682
|
+
objType = infer.def.parsePath(path, file.scope).getObjType();
|
|
683
|
+
prop = word;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (prop != null) {
|
|
688
|
+
srv.cx.completingProperty = prop;
|
|
689
|
+
|
|
690
|
+
if (objType) infer.forAllPropertiesOf(objType, gather);
|
|
691
|
+
|
|
692
|
+
if (!completions.length && query.guess !== false && objType && objType.guessProperties)
|
|
693
|
+
objType.guessProperties(function(p, o, d) {if (p != prop && p != "✖") gather(p, o, d);});
|
|
694
|
+
if (!completions.length && word.length >= 2 && query.guess !== false)
|
|
695
|
+
for (var prop in srv.cx.props) gather(prop, srv.cx.props[prop][0], 0);
|
|
696
|
+
hookname = "memberCompletion";
|
|
697
|
+
} else {
|
|
698
|
+
infer.forAllLocalsAt(file.ast, wordStart, file.scope, gather);
|
|
699
|
+
if (query.includeKeywords) jsKeywords.forEach(function(kw) {
|
|
700
|
+
gather(kw, null, 0, function(rec) { rec.isKeyword = true; });
|
|
701
|
+
});
|
|
702
|
+
hookname = "variableCompletion";
|
|
703
|
+
}
|
|
704
|
+
if (srv.passes[hookname])
|
|
705
|
+
srv.passes[hookname].forEach(function(hook) {hook(file, wordStart, wordEnd, gather);});
|
|
706
|
+
|
|
707
|
+
if (query.sort !== false) completions.sort(compareCompletions);
|
|
708
|
+
srv.cx.completingProperty = null;
|
|
709
|
+
|
|
710
|
+
return {start: outputPos(query, file, wordStart),
|
|
711
|
+
end: outputPos(query, file, wordEnd),
|
|
712
|
+
isProperty: !!prop,
|
|
713
|
+
isObjectKey: !!isKey,
|
|
714
|
+
completions: completions};
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function findProperties(srv, query) {
|
|
718
|
+
var prefix = query.prefix, found = [];
|
|
719
|
+
for (var prop in srv.cx.props)
|
|
720
|
+
if (prop != "<i>" && (!prefix || prop.indexOf(prefix) === 0)) found.push(prop);
|
|
721
|
+
if (query.sort !== false) found.sort(compareCompletions);
|
|
722
|
+
return {completions: found};
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
var findExpr = exports.findQueryExpr = function(file, query, wide) {
|
|
726
|
+
if (query.end == null) throw ternError("missing .query.end field");
|
|
727
|
+
|
|
728
|
+
if (query.variable) {
|
|
729
|
+
var scope = infer.scopeAt(file.ast, resolvePos(file, query.end), file.scope);
|
|
730
|
+
return {node: {type: "Identifier", name: query.variable, start: query.end, end: query.end + 1},
|
|
731
|
+
state: scope};
|
|
732
|
+
} else {
|
|
733
|
+
var start = query.start && resolvePos(file, query.start), end = resolvePos(file, query.end);
|
|
734
|
+
var expr = infer.findExpressionAt(file.ast, start, end, file.scope);
|
|
735
|
+
if (expr) return expr;
|
|
736
|
+
expr = infer.findExpressionAround(file.ast, start, end, file.scope);
|
|
737
|
+
if (expr && (expr.node.type == "ObjectExpression" || wide ||
|
|
738
|
+
(start == null ? end : start) - expr.node.start < 20 || expr.node.end - end < 20))
|
|
739
|
+
return expr;
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
function findExprOrThrow(file, query, wide) {
|
|
745
|
+
var expr = findExpr(file, query, wide);
|
|
746
|
+
if (expr) return expr;
|
|
747
|
+
throw ternError("No expression at the given position.");
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function ensureObj(tp) {
|
|
751
|
+
if (!tp || !(tp = tp.getType()) || !(tp instanceof infer.Obj)) return null;
|
|
752
|
+
return tp;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function findExprType(srv, query, file, expr) {
|
|
756
|
+
var type;
|
|
757
|
+
if (expr) {
|
|
758
|
+
infer.resetGuessing();
|
|
759
|
+
type = infer.expressionType(expr);
|
|
760
|
+
}
|
|
761
|
+
if (srv.passes["typeAt"]) {
|
|
762
|
+
var pos = resolvePos(file, query.end);
|
|
763
|
+
srv.passes["typeAt"].forEach(function(hook) {
|
|
764
|
+
type = hook(file, pos, expr, type);
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
if (!type) throw ternError("No type found at the given position.");
|
|
768
|
+
|
|
769
|
+
var objProp;
|
|
770
|
+
if (expr.node.type == "ObjectExpression" && query.end != null &&
|
|
771
|
+
(objProp = pointInProp(expr.node, resolvePos(file, query.end)))) {
|
|
772
|
+
var name = objProp.key.name;
|
|
773
|
+
var fromCx = ensureObj(infer.typeFromContext(file.ast, expr));
|
|
774
|
+
if (fromCx && fromCx.hasProp(name)) {
|
|
775
|
+
type = fromCx.hasProp(name);
|
|
776
|
+
} else {
|
|
777
|
+
var fromLocal = ensureObj(type);
|
|
778
|
+
if (fromLocal && fromLocal.hasProp(name))
|
|
779
|
+
type = fromLocal.hasProp(name);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return type;
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
function findTypeAt(srv, query, file) {
|
|
786
|
+
var expr = findExpr(file, query), exprName;
|
|
787
|
+
var type = findExprType(srv, query, file, expr), exprType = type;
|
|
788
|
+
if (query.preferFunction)
|
|
789
|
+
type = type.getFunctionType() || type.getType();
|
|
790
|
+
else
|
|
791
|
+
type = type.getType();
|
|
792
|
+
|
|
793
|
+
if (expr) {
|
|
794
|
+
if (expr.node.type == "Identifier")
|
|
795
|
+
exprName = expr.node.name;
|
|
796
|
+
else if (expr.node.type == "MemberExpression" && !expr.node.computed)
|
|
797
|
+
exprName = expr.node.property.name;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (query.depth != null && typeof query.depth != "number")
|
|
801
|
+
throw ternError(".query.depth must be a number");
|
|
802
|
+
|
|
803
|
+
var result = {guess: infer.didGuess(),
|
|
804
|
+
type: infer.toString(exprType, query.depth),
|
|
805
|
+
name: type && type.name,
|
|
806
|
+
exprName: exprName};
|
|
807
|
+
if (type) storeTypeDocs(type, result);
|
|
808
|
+
if (!result.doc && exprType.doc) result.doc = exprType.doc;
|
|
809
|
+
|
|
810
|
+
return clean(result);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function findDocs(srv, query, file) {
|
|
814
|
+
var expr = findExpr(file, query);
|
|
815
|
+
var type = findExprType(srv, query, file, expr);
|
|
816
|
+
var result = {url: type.url, doc: type.doc, type: infer.toString(type)};
|
|
817
|
+
var inner = type.getType();
|
|
818
|
+
if (inner) storeTypeDocs(inner, result);
|
|
819
|
+
return clean(result);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function storeTypeDocs(type, out) {
|
|
823
|
+
if (!out.url) out.url = type.url;
|
|
824
|
+
if (!out.doc) out.doc = type.doc;
|
|
825
|
+
if (!out.origin) out.origin = type.origin;
|
|
826
|
+
var ctor, boring = infer.cx().protos;
|
|
827
|
+
if (!out.url && !out.doc && type.proto && (ctor = type.proto.hasCtor) &&
|
|
828
|
+
type.proto != boring.Object && type.proto != boring.Function && type.proto != boring.Array) {
|
|
829
|
+
out.url = ctor.url;
|
|
830
|
+
out.doc = ctor.doc;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
var getSpan = exports.getSpan = function(obj) {
|
|
835
|
+
if (!obj.origin) return;
|
|
836
|
+
if (obj.originNode) {
|
|
837
|
+
var node = obj.originNode;
|
|
838
|
+
if (/^Function/.test(node.type) && node.id) node = node.id;
|
|
839
|
+
return {origin: obj.origin, node: node};
|
|
840
|
+
}
|
|
841
|
+
if (obj.span) return {origin: obj.origin, span: obj.span};
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
var storeSpan = exports.storeSpan = function(srv, query, span, target) {
|
|
845
|
+
target.origin = span.origin;
|
|
846
|
+
if (span.span) {
|
|
847
|
+
var m = /^(\d+)\[(\d+):(\d+)\]-(\d+)\[(\d+):(\d+)\]$/.exec(span.span);
|
|
848
|
+
target.start = query.lineCharPositions ? {line: Number(m[2]), ch: Number(m[3])} : Number(m[1]);
|
|
849
|
+
target.end = query.lineCharPositions ? {line: Number(m[5]), ch: Number(m[6])} : Number(m[4]);
|
|
850
|
+
} else {
|
|
851
|
+
var file = srv.findFile(span.origin);
|
|
852
|
+
target.start = outputPos(query, file, span.node.start);
|
|
853
|
+
target.end = outputPos(query, file, span.node.end);
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
function findDef(srv, query, file) {
|
|
858
|
+
var expr = findExpr(file, query);
|
|
859
|
+
var type = findExprType(srv, query, file, expr);
|
|
860
|
+
if (infer.didGuess()) return {};
|
|
861
|
+
|
|
862
|
+
var span = getSpan(type);
|
|
863
|
+
var result = {url: type.url, doc: type.doc, origin: type.origin};
|
|
864
|
+
|
|
865
|
+
if (type.types) for (var i = type.types.length - 1; i >= 0; --i) {
|
|
866
|
+
var tp = type.types[i];
|
|
867
|
+
storeTypeDocs(tp, result);
|
|
868
|
+
if (!span) span = getSpan(tp);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (span && span.node) { // refers to a loaded file
|
|
872
|
+
var spanFile = span.node.sourceFile || srv.findFile(span.origin);
|
|
873
|
+
var start = outputPos(query, spanFile, span.node.start), end = outputPos(query, spanFile, span.node.end);
|
|
874
|
+
result.start = start; result.end = end;
|
|
875
|
+
result.file = span.origin;
|
|
876
|
+
var cxStart = Math.max(0, span.node.start - 50);
|
|
877
|
+
result.contextOffset = span.node.start - cxStart;
|
|
878
|
+
result.context = spanFile.text.slice(cxStart, cxStart + 50);
|
|
879
|
+
} else if (span) { // external
|
|
880
|
+
result.file = span.origin;
|
|
881
|
+
storeSpan(srv, query, span, result);
|
|
882
|
+
}
|
|
883
|
+
return clean(result);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function findRefsToVariable(srv, query, file, expr, checkShadowing) {
|
|
887
|
+
var name = expr.node.name;
|
|
888
|
+
|
|
889
|
+
for (var scope = expr.state; scope && !(name in scope.props); scope = scope.prev) {}
|
|
890
|
+
if (!scope) throw ternError("Could not find a definition for " + name + " " + !!srv.cx.topScope.props.x);
|
|
891
|
+
|
|
892
|
+
var type, refs = [];
|
|
893
|
+
function storeRef(file) {
|
|
894
|
+
return function(node, scopeHere) {
|
|
895
|
+
if (checkShadowing) for (var s = scopeHere; s != scope; s = s.prev) {
|
|
896
|
+
var exists = s.hasProp(checkShadowing);
|
|
897
|
+
if (exists)
|
|
898
|
+
throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would make a variable at line " +
|
|
899
|
+
(asLineChar(file, node.start).line + 1) + " point to the definition at line " +
|
|
900
|
+
(asLineChar(file, exists.name.start).line + 1));
|
|
901
|
+
}
|
|
902
|
+
refs.push({file: file.name,
|
|
903
|
+
start: outputPos(query, file, node.start),
|
|
904
|
+
end: outputPos(query, file, node.end)});
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if (scope.originNode) {
|
|
909
|
+
type = "local";
|
|
910
|
+
if (checkShadowing) {
|
|
911
|
+
for (var prev = scope.prev; prev; prev = prev.prev)
|
|
912
|
+
if (checkShadowing in prev.props) break;
|
|
913
|
+
if (prev) infer.findRefs(scope.originNode, scope, checkShadowing, prev, function(node) {
|
|
914
|
+
throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would shadow the definition used at line " +
|
|
915
|
+
(asLineChar(file, node.start).line + 1));
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
infer.findRefs(scope.originNode, scope, name, scope, storeRef(file));
|
|
919
|
+
} else {
|
|
920
|
+
type = "global";
|
|
921
|
+
for (var i = 0; i < srv.files.length; ++i) {
|
|
922
|
+
var cur = srv.files[i];
|
|
923
|
+
infer.findRefs(cur.ast, cur.scope, name, scope, storeRef(cur));
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
return {refs: refs, type: type, name: name};
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function findRefsToProperty(srv, query, expr, prop) {
|
|
931
|
+
var objType = infer.expressionType(expr).getObjType();
|
|
932
|
+
if (!objType) throw ternError("Couldn't determine type of base object.");
|
|
933
|
+
|
|
934
|
+
var refs = [];
|
|
935
|
+
function storeRef(file) {
|
|
936
|
+
return function(node) {
|
|
937
|
+
refs.push({file: file.name,
|
|
938
|
+
start: outputPos(query, file, node.start),
|
|
939
|
+
end: outputPos(query, file, node.end)});
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
for (var i = 0; i < srv.files.length; ++i) {
|
|
943
|
+
var cur = srv.files[i];
|
|
944
|
+
infer.findPropRefs(cur.ast, cur.scope, objType, prop.name, storeRef(cur));
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
return {refs: refs, name: prop.name};
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
function findRefs(srv, query, file) {
|
|
951
|
+
var expr = findExprOrThrow(file, query, true);
|
|
952
|
+
if (expr.node.type == "Identifier") {
|
|
953
|
+
return findRefsToVariable(srv, query, file, expr);
|
|
954
|
+
} else if (expr.node.type == "MemberExpression" && !expr.node.computed) {
|
|
955
|
+
var p = expr.node.property;
|
|
956
|
+
expr.node = expr.node.object;
|
|
957
|
+
return findRefsToProperty(srv, query, expr, p);
|
|
958
|
+
} else if (expr.node.type == "ObjectExpression") {
|
|
959
|
+
var pos = resolvePos(file, query.end);
|
|
960
|
+
for (var i = 0; i < expr.node.properties.length; ++i) {
|
|
961
|
+
var k = expr.node.properties[i].key;
|
|
962
|
+
if (k.start <= pos && k.end >= pos)
|
|
963
|
+
return findRefsToProperty(srv, query, expr, k);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
throw ternError("Not at a variable or property name.");
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
function buildRename(srv, query, file) {
|
|
970
|
+
if (typeof query.newName != "string") throw ternError(".query.newName should be a string");
|
|
971
|
+
var expr = findExprOrThrow(file, query);
|
|
972
|
+
if (!expr || expr.node.type != "Identifier") throw ternError("Not at a variable.");
|
|
973
|
+
|
|
974
|
+
var data = findRefsToVariable(srv, query, file, expr, query.newName), refs = data.refs;
|
|
975
|
+
delete data.refs;
|
|
976
|
+
data.files = srv.files.map(function(f){return f.name;});
|
|
977
|
+
|
|
978
|
+
var changes = data.changes = [];
|
|
979
|
+
for (var i = 0; i < refs.length; ++i) {
|
|
980
|
+
var use = refs[i];
|
|
981
|
+
use.text = query.newName;
|
|
982
|
+
changes.push(use);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
return data;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
function listFiles(srv) {
|
|
989
|
+
return {files: srv.files.map(function(f){return f.name;})};
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
exports.version = "0.11.1";
|
|
993
|
+
});
|