OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * @interface | |
7 */ | |
8 WebInspector.LanguageService = function(){}; | |
9 WebInspector.LanguageService.prototype = { | |
10 /** | |
11 * @return {string} | |
12 */ | |
13 get name() {}, | |
pfeldman
2015/08/13 21:15:46
We don't use getters for the new code.
wes
2015/08/14 00:55:05
Oh, okay. I had seen them around elsewhere and was
pfeldman
2015/08/17 21:15:51
I know, we are slowly getting rid of them wherever
| |
14 | |
15 /** | |
16 * @param {string} capability | |
17 * @return {boolean} | |
18 */ | |
19 handlesCapability: function(capability) { return false; } | |
pfeldman
2015/08/13 21:15:46
capabilities: function getter instead?
wes
2015/08/14 00:55:05
Possible. I can't really think of a use-case where
| |
20 } | |
21 | |
22 /** | |
23 * The language service capabilities list determines what actions are added | |
24 * to the LanguageService and LanguageService.Handles objects, in addition | |
25 * to what methods are searched for on providers to fulfill those capabilities | |
26 */ | |
27 WebInspector.LanguageService.Capabilities = { | |
28 Transpile: "transpile", | |
pfeldman
2015/08/13 21:15:46
4 space indent everywhere.
wes
2015/08/14 00:55:05
Ach. At least I was internally consistent to the f
pfeldman
2015/08/17 21:15:51
Nope, we don't have a linter :( We use the Blink c
| |
29 PopulateContextMenu: "populateContextMenu", | |
30 Completions: "completions", | |
31 DebuggerCompletions: "debuggerCompletions", | |
32 }; | |
33 | |
34 /** | |
35 * @constructor | |
36 * @extends {WebInspector.Object} | |
37 */ | |
38 WebInspector.LanguageServiceManager = function() { | |
39 this._override = {}; | |
40 this._servicesByMime = {}; | |
41 | |
42 for (var key in WebInspector.LanguageService.Capabilities) { | |
43 var actionName = WebInspector.LanguageService.Capabilities[key]; | |
44 this._servicesByMime[Symbol.for(key)] = {}; | |
45 WebInspector.LanguageService.prototype[actionName] = function() {}; | |
46 } | |
47 | |
48 var _this = this; | |
49 function makeHandlesChecker(key) { | |
50 var actionName = WebInspector.LanguageService.Capabilities[key]; | |
51 return function(mime) { | |
52 var override = _this._override[actionName]; | |
53 if (typeof override !== "undefined") { | |
54 return override; | |
55 } | |
56 var service = _this._servicesByMime[Symbol.for(key)][mim e]; | |
57 return service; | |
58 }; | |
59 } | |
60 | |
61 function makeCapabilityAction(key) { | |
62 var actionName = WebInspector.LanguageService.Capabilities[key]; | |
63 return function(mime) { | |
64 var override = _this._override[actionName]; | |
65 if (typeof override !== "undefined") { | |
66 if (typeof override === "boolean") { | |
67 if (override) { | |
68 return Promise.resolve(); | |
69 } else { | |
70 return Promise.reject("No langua ge service present to handle \""+(key)+"\" for mime \""+(mime)+"\"."); | |
71 } | |
72 } | |
73 return override[actionName].apply(override, Arra y.prototype.slice.call(arguments, 1)); | |
74 } | |
75 if (!_this.handles[actionName](mime)) { | |
76 return Promise.reject("No language service prese nt to handle \""+(key)+"\" for mime \""+(mime)+"\"."); | |
77 } | |
78 var service = _this._servicesByMime[Symbol.for(key)][mim e]; | |
79 return service[actionName].apply(service, Array.prototyp e.slice.call(arguments, 1)); | |
80 }; | |
81 } | |
82 | |
83 for (var key in WebInspector.LanguageService.Capabilities) { | |
84 _this.handles[WebInspector.LanguageService.Capabilities[key]] = makeHandlesChecker(key); | |
85 _this[WebInspector.LanguageService.Capabilities[key]] = makeCapa bilityAction(key); | |
86 } | |
87 }; | |
88 | |
89 WebInspector.LanguageServiceManager.Events = { | |
90 ServiceAdded: "onServiceAdded", | |
91 InferredMimeUpdated: "onInferredMimeUpdated" | |
92 }; | |
93 | |
94 /** | |
95 * Stub external API here to satisfy closure compiler - actual implementations | |
96 * are done in the ctor above. Not sure if there's a better way to type this, wh ile | |
97 * still making it simple to 'declare' a new optional member to a service | |
98 */ | |
99 WebInspector.LanguageServiceManager.prototype = { | |
100 | |
101 handles: { | |
102 /** | |
103 * @param {string} mime | |
104 * @return {?WebInspector.LanguageService} | |
105 */ | |
106 transpile: function(mime) {return null;}, | |
107 | |
108 /** | |
109 * @param {string} mime | |
110 * @return {?WebInspector.LanguageService} | |
111 */ | |
112 populateContextMenu: function(mime) {return null;}, | |
113 | |
114 /** | |
115 * @param {string} mime | |
116 * @return {?WebInspector.LanguageService} | |
117 */ | |
118 completions: function(mime) {return null;}, | |
119 | |
120 /** | |
121 * @param {string} mime | |
122 * @return {?WebInspector.LanguageService} | |
123 */ | |
124 debuggerCompletions: function(mime) {return null;} | |
125 }, | |
126 | |
127 /** | |
128 * @param {!Array<string>} mimes | |
129 * @param {!WebInspector.LanguageService} service | |
130 */ | |
131 register: function(mimes, service) { | |
132 for (var index = 0; index < mimes.length; index++) { | |
133 var mime = mimes[index]; | |
134 for (var key in WebInspector.LanguageService.Capabilitie s) { | |
135 var capability = WebInspector.LanguageService.Ca pabilities[key]; | |
136 if (service.handlesCapability(capability)) { | |
137 this._servicesByMime[Symbol.for(key)][mi me] = service; | |
138 } | |
139 } | |
140 | |
141 this._servicesByMime[mime] = service; | |
142 } | |
143 this._onServiceAdded(service, mimes); | |
144 }, | |
145 | |
146 _onServiceAdded(service, mimes) { | |
147 this.dispatchEventToListeners(WebInspector.LanguageServiceManage r.Events.ServiceAdded, {service, mimes}); | |
148 }, | |
149 | |
150 /** | |
151 * @param {string} capability | |
152 * @param {!WebInspector.LanguageService=} override | |
153 */ | |
154 disableContextualHandling: function(capability, override) { | |
155 this._override[capability] = override || false; | |
156 }, | |
157 | |
158 /** | |
159 * @param {string} capability | |
160 */ | |
161 enableContextualHandling: function(capability) { | |
162 delete this._override[capability]; | |
163 }, | |
164 | |
165 /** | |
166 * @param {string=} capability | |
167 * @return {!Array<!WebInspector.LanguageService>} | |
168 */ | |
169 registered: function(capability) { | |
170 return [...new Set(Object.keys(capability ? this._servicesByMime [Symbol.for(capability)]||{} : this._servicesByMime).map(k => capability ? this. _servicesByMime[Symbol.for(capability)][k] : this._servicesByMime[k]))]; | |
171 }, | |
172 | |
173 /** | |
174 * @param {string} mime | |
175 * @return {?WebInspector.LanguageService} | |
176 */ | |
177 for: function(mime) { | |
178 return this._servicesByMime[mime]; | |
179 }, | |
180 | |
181 /** | |
182 * @param {string} mime | |
183 * @param {string} input | |
184 * @param {?{source: string, line: number, column: number}=} location | |
185 * @return {!Promise<string>} | |
186 */ | |
187 transpile: function(mime, input, location) {return Promise.reject();}, | |
188 | |
189 /** | |
190 * @param {string} mime | |
191 * @param {{source: string, line: number, column: number}} location | |
192 * @return {!Promise<!Array<{text: string, callback: !Function}>>} | |
193 */ | |
194 populateContextMenu: function(mime, location) {return Promise.reject();} , | |
195 | |
196 /** | |
197 * @param {string} mime | |
198 * @param {{source: string, line: number, column: number}} location | |
199 * @param {string} prefix | |
200 * @return {!Promise<!Array<{text: string, details: !Promise<{detail: st ring, description: string}>}>>} | |
201 */ | |
202 completions: function(mime, location, prefix) {return Promise.reject();} , | |
203 | |
204 /** | |
205 * @param {string} mime | |
206 * @param {string} content | |
207 * @param {number} cursorOffset | |
208 * @param {string} prefix | |
209 * @param {{source: string, line: number, column: number}=} context | |
210 * @return {!Promise<!Array<{text: string, details: !Promise<{detail: st ring, description: string}>}>>} | |
211 */ | |
212 debuggerCompletions: function(mime, content, cursorOffset, prefix, conte xt) {return Promise.reject();}, | |
213 | |
214 updateInferredMime: function() { | |
215 this.dispatchEventToListeners(WebInspector.LanguageServiceManage r.Events.InferredMimeUpdated); | |
216 }, | |
217 | |
218 __proto__: WebInspector.Object.prototype | |
219 }; | |
220 | |
221 /** | |
222 * @constructor | |
223 * @implements {WebInspector.LanguageService} | |
224 */ | |
225 WebInspector.LanguageService.CallbackDelegate = function(name, capabilities) { | |
226 this._name = name; | |
227 | |
228 for (var key in WebInspector.LanguageService.Capabilities) { | |
229 var actionName = WebInspector.LanguageService.Capabilities[key]; | |
230 if (!!capabilities[actionName]) { | |
231 this.appendActionHandler(actionName, capabilities[action Name]); | |
232 } | |
233 } | |
234 }; | |
235 | |
236 WebInspector.LanguageService.CallbackDelegate.prototype = { | |
237 /** | |
238 * @override | |
239 * @return {string} | |
240 */ | |
241 get name() { | |
242 return this._name; | |
243 }, | |
244 | |
245 /** | |
246 * Given an action and a function expecting a callback for | |
247 * its final argument, modifies it to return a promise and | |
248 * appends it to the language service | |
249 * | |
250 * @param {string} capability | |
251 * @param {!Function} func | |
252 */ | |
253 appendActionHandler: function(capability, func) { | |
254 var _this = this; | |
255 this[capability] = function() { | |
256 var args = arguments; | |
257 return new Promise(function(resolve, reject) { | |
258 func.call(_this, ...args, resolve); | |
259 }); | |
260 }; | |
261 }, | |
262 | |
263 /** | |
264 * @override | |
265 * @param {string} capability | |
266 * @return {boolean} | |
267 */ | |
268 handlesCapability: function(capability) { | |
269 return !!this[capability]; | |
270 } | |
271 }; | |
272 | |
273 /** | |
274 * @constructor | |
275 * @param {!Element} selectElement | |
276 * @param {!Array<string>} capabilities | |
277 */ | |
278 WebInspector.LanguageServiceModel = function(selectElement, capabilities) { | |
279 this._capabilities = capabilities; | |
280 this._selectElement = selectElement; | |
281 var newOption = createElement("option"); | |
282 newOption.__type = WebInspector.LanguageServiceModel.OptionType.Inferred; | |
283 newOption.text = WebInspector.UIString("%s (inferred)", WebInspector.UIStrin g("javascript")); | |
284 this._selectElement.appendChild(newOption); | |
285 | |
286 var jsOption = createElement("option"); | |
287 jsOption.__type = WebInspector.LanguageServiceModel.OptionType.Builtin; | |
288 jsOption.text = WebInspector.UIString("javascript"); | |
289 this._selectElement.appendChild(jsOption); | |
290 | |
291 WebInspector.languageService.registered().forEach(service => { | |
292 this.addOption(service); | |
293 }); | |
294 | |
295 WebInspector.languageService.addEventListener(WebInspector.LanguageServi ceManager.Events.ServiceAdded, this._onServiceAdded, this); | |
296 WebInspector.languageService.addEventListener(WebInspector.LanguageServi ceManager.Events.InferredMimeUpdated, this._onViewSelected, this); | |
297 this._selectElement.addEventListener("change", this._languageServiceChan ged.bind(this), false); | |
298 }; | |
299 | |
300 WebInspector.LanguageServiceModel.prototype = { | |
301 /** | |
302 * @param {!WebInspector.LanguageService} service | |
303 */ | |
304 addOption: function(service) { | |
305 var newOption = createElement("option"); | |
306 newOption.__service = service; | |
307 newOption.__type = WebInspector.LanguageServiceModel.OptionType. External; | |
308 newOption.text = service.name | |
309 this._selectElement.appendChild(newOption); | |
310 }, | |
311 | |
312 _onServiceAdded: function(evt) { | |
313 var service = /** @type {!WebInspector.LanguageService} */(evt.d ata.service); | |
314 this.addOption(service); | |
315 }, | |
316 | |
317 _onViewSelected: function(evt) { | |
318 var mime = WebInspector.ResourceType.fromActivePanel(); | |
319 var service = WebInspector.languageService.for(mime); | |
320 var name = WebInspector.UIString("javascript"); | |
321 if (service) { | |
322 name = service.name; | |
323 } | |
324 var opts = this._selectElement.options; | |
325 for (var i = 0; i < opts.length; i++) { | |
326 var option = opts[i]; | |
327 if (option.__type === WebInspector.LanguageServiceModel. OptionType.Inferred) { | |
328 option.text = WebInspector.UIString("%s (inferre d)", name); | |
329 } | |
330 } | |
331 }, | |
332 | |
333 _languageServiceChanged: function() { | |
334 var option = this._selectedOption(); | |
335 switch(option.__type) { | |
336 case WebInspector.LanguageServiceModel.OptionType.Inferr ed: | |
337 this._capabilities.forEach(c => { | |
338 WebInspector.languageService.enableContextualHan dling(c); | |
339 }); | |
340 break; | |
341 case WebInspector.LanguageServiceModel.OptionType.Builti n: | |
342 this._capabilities.forEach(c => { | |
343 WebInspector.languageService.disableContextualHa ndling(c); | |
344 }); | |
345 break; | |
346 case WebInspector.LanguageServiceModel.OptionType.Extern al: | |
347 this._capabilities.forEach(c => { | |
348 WebInspector.languageService.disableContextualHa ndling(c, option.__service); | |
349 }); | |
350 break; | |
351 default: | |
352 break; | |
353 } | |
354 }, | |
355 | |
356 /** | |
357 * @return {?Element} | |
358 */ | |
359 _selectedOption: function() | |
360 { | |
361 if (this._selectElement.selectedIndex >= 0) | |
362 return this._selectElement[this._selectElement.selectedIndex]; | |
363 return null; | |
364 } | |
365 }; | |
366 | |
367 WebInspector.LanguageServiceModel.OptionType = { | |
368 Inferred: "Inferred", | |
369 Builtin: "Builtin", | |
370 External: "External" | |
371 }; | |
372 | |
373 WebInspector.languageService = new WebInspector.LanguageServiceManager(); | |
OLD | NEW |