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 name: function() { throw new Error("Not implemented"); }, |
| 14 |
| 15 /** |
| 16 * @return {!Array<string>} |
| 17 */ |
| 18 capabilities: function() { return []; } |
| 19 } |
| 20 |
| 21 /** |
| 22 * The language service capabilities list determines what actions are added |
| 23 * to the LanguageService and LanguageService.Handles objects, in addition |
| 24 * to what methods are searched for on providers to fulfill those capabilities |
| 25 */ |
| 26 WebInspector.LanguageService.Capabilities = { |
| 27 Transpile: "transpile", |
| 28 PopulateContextMenu: "populateContextMenu", |
| 29 Completions: "completions", |
| 30 DebuggerCompletions: "debuggerCompletions", |
| 31 }; |
| 32 |
| 33 /** |
| 34 * @constructor |
| 35 * @extends {WebInspector.Object} |
| 36 */ |
| 37 WebInspector.LanguageServiceManager = function() { |
| 38 this._override = {}; |
| 39 this._servicesByMime = {}; |
| 40 |
| 41 for (var key in WebInspector.LanguageService.Capabilities) { |
| 42 var actionName = WebInspector.LanguageService.Capabilities[key]; |
| 43 this._servicesByMime[Symbol.for(key)] = {}; |
| 44 WebInspector.LanguageService.prototype[actionName] = function() {}; |
| 45 } |
| 46 |
| 47 var _this = this; |
| 48 function makeHandlesChecker(key) { |
| 49 var actionName = WebInspector.LanguageService.Capabilities[key]; |
| 50 return function(mime) { |
| 51 var override = _this._override[actionName]; |
| 52 if (typeof override !== "undefined") { |
| 53 return override; |
| 54 } |
| 55 var service = _this._servicesByMime[Symbol.for(key)][mime]; |
| 56 return service; |
| 57 }; |
| 58 } |
| 59 |
| 60 function makeCapabilityAction(key) { |
| 61 var actionName = WebInspector.LanguageService.Capabilities[key]; |
| 62 return function(mime) { |
| 63 var override = _this._override[actionName]; |
| 64 if (typeof override !== "undefined") { |
| 65 if (typeof override === "boolean") { |
| 66 if (override) { |
| 67 return Promise.resolve(); |
| 68 } else { |
| 69 return Promise.reject("No language service present to ha
ndle \""+(key)+"\" for mime \""+(mime)+"\"."); |
| 70 } |
| 71 } |
| 72 return override[actionName].apply(override, Array.prototype.slic
e.call(arguments, 1)); |
| 73 } |
| 74 if (!_this.handles[actionName](mime)) { |
| 75 return Promise.reject("No language service present to handle \""
+(key)+"\" for mime \""+(mime)+"\"."); |
| 76 } |
| 77 var service = _this._servicesByMime[Symbol.for(key)][mime]; |
| 78 return service[actionName].apply(service, Array.prototype.slice.call
(arguments, 1)); |
| 79 }; |
| 80 } |
| 81 |
| 82 for (var key in WebInspector.LanguageService.Capabilities) { |
| 83 _this.handles[WebInspector.LanguageService.Capabilities[key]] = makeHand
lesChecker(key); |
| 84 _this[WebInspector.LanguageService.Capabilities[key]] = makeCapabilityAc
tion(key); |
| 85 } |
| 86 }; |
| 87 |
| 88 WebInspector.LanguageServiceManager.Events = { |
| 89 ServiceAdded: "onServiceAdded", |
| 90 InferredMimeUpdated: "onInferredMimeUpdated" |
| 91 }; |
| 92 |
| 93 /** |
| 94 * Stub external API here to satisfy closure compiler - actual implementations |
| 95 * are done in the ctor above. Not sure if there's a better way to type this, wh
ile |
| 96 * still making it simple to 'declare' a new optional member to a service |
| 97 */ |
| 98 WebInspector.LanguageServiceManager.prototype = { |
| 99 |
| 100 handles: { |
| 101 /** |
| 102 * @param {string} mime |
| 103 * @return {?WebInspector.LanguageService} |
| 104 */ |
| 105 transpile: function(mime) {return null;}, |
| 106 |
| 107 /** |
| 108 * @param {string} mime |
| 109 * @return {?WebInspector.LanguageService} |
| 110 */ |
| 111 populateContextMenu: function(mime) {return null;}, |
| 112 |
| 113 /** |
| 114 * @param {string} mime |
| 115 * @return {?WebInspector.LanguageService} |
| 116 */ |
| 117 completions: function(mime) {return null;}, |
| 118 |
| 119 /** |
| 120 * @param {string} mime |
| 121 * @return {?WebInspector.LanguageService} |
| 122 */ |
| 123 debuggerCompletions: function(mime) {return null;} |
| 124 }, |
| 125 |
| 126 /** |
| 127 * @param {!Array<string>} mimes |
| 128 * @param {!WebInspector.LanguageService} service |
| 129 */ |
| 130 register: function(mimes, service) { |
| 131 for (var index = 0; index < mimes.length; index++) { |
| 132 var mime = mimes[index]; |
| 133 for (var key in WebInspector.LanguageService.Capabilities) { |
| 134 var capability = WebInspector.LanguageService.Capabilities[key]; |
| 135 if (service.capabilities().indexOf(capability) !== -1) { |
| 136 this._servicesByMime[Symbol.for(key)][mime] = service; |
| 137 } |
| 138 } |
| 139 |
| 140 this._servicesByMime[mime] = service; |
| 141 } |
| 142 this._onServiceAdded(service, mimes); |
| 143 }, |
| 144 |
| 145 _onServiceAdded(service, mimes) { |
| 146 this.dispatchEventToListeners(WebInspector.LanguageServiceManager.Events
.ServiceAdded, {service, mimes}); |
| 147 }, |
| 148 |
| 149 /** |
| 150 * @param {string} capability |
| 151 * @param {!WebInspector.LanguageService=} override |
| 152 */ |
| 153 disableContextualHandling: function(capability, override) { |
| 154 this._override[capability] = override || false; |
| 155 }, |
| 156 |
| 157 /** |
| 158 * @param {string} capability |
| 159 */ |
| 160 enableContextualHandling: function(capability) { |
| 161 delete this._override[capability]; |
| 162 }, |
| 163 |
| 164 /** |
| 165 * @param {string=} capability |
| 166 * @return {!Array<!WebInspector.LanguageService>} |
| 167 */ |
| 168 registered: function(capability) { |
| 169 return [...new Set(Object.keys(capability ? this._servicesByMime[Symbol.
for(capability)]||{} : this._servicesByMime).map((function(k){ return capability
? this._servicesByMime[Symbol.for(capability)][k] : this._servicesByMime[k]; })
.bind(this)))]; |
| 170 }, |
| 171 |
| 172 /** |
| 173 * @param {string} mime |
| 174 * @return {?WebInspector.LanguageService} |
| 175 */ |
| 176 for: function(mime) { |
| 177 return this._servicesByMime[mime]; |
| 178 }, |
| 179 |
| 180 /** |
| 181 * @param {string} mime |
| 182 * @param {string} input |
| 183 * @param {?{source: string, line: number, column: number}=} location |
| 184 * @return {!Promise<string>} |
| 185 */ |
| 186 transpile: function(mime, input, location) {return Promise.reject();}, |
| 187 |
| 188 /** |
| 189 * @param {string} mime |
| 190 * @param {{source: string, line: number, column: number}} location |
| 191 * @return {!Promise<!Array<{text: string, callback: !Function}>>} |
| 192 */ |
| 193 populateContextMenu: function(mime, location) {return Promise.reject();}, |
| 194 |
| 195 /** |
| 196 * @param {string} mime |
| 197 * @param {{source: string, line: number, column: number}} location |
| 198 * @param {string} prefix |
| 199 * @return {!Promise<!Array<{text: string, details: !Promise<{detail: string
, description: string}>}>>} |
| 200 */ |
| 201 completions: function(mime, location, prefix) {return Promise.reject();}, |
| 202 |
| 203 /** |
| 204 * @param {string} mime |
| 205 * @param {string} content |
| 206 * @param {number} cursorOffset |
| 207 * @param {string} prefix |
| 208 * @param {{source: string, line: number, column: number}=} context |
| 209 * @return {!Promise<!Array<{text: string, details: !Promise<{detail: string
, description: string}>}>>} |
| 210 */ |
| 211 debuggerCompletions: function(mime, content, cursorOffset, prefix, context)
{return Promise.reject();}, |
| 212 |
| 213 updateInferredMime: function() { |
| 214 this.dispatchEventToListeners(WebInspector.LanguageServiceManager.Events
.InferredMimeUpdated); |
| 215 }, |
| 216 |
| 217 __proto__: WebInspector.Object.prototype |
| 218 }; |
| 219 |
| 220 /** |
| 221 * @constructor |
| 222 * @implements {WebInspector.LanguageService} |
| 223 */ |
| 224 WebInspector.LanguageService.CallbackDelegate = function(name, capabilities) { |
| 225 this._name = name; |
| 226 this._capabilities = []; |
| 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[actionName]); |
| 232 } |
| 233 } |
| 234 }; |
| 235 |
| 236 WebInspector.LanguageService.CallbackDelegate.prototype = { |
| 237 /** |
| 238 * @override |
| 239 * @return {string} |
| 240 */ |
| 241 name: function() { |
| 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._capabilities.push(capability); |
| 256 this[capability] = function() { |
| 257 var args = arguments; |
| 258 return new Promise(function(resolve, reject) { |
| 259 func.call(_this, ...args, resolve); |
| 260 }); |
| 261 }; |
| 262 }, |
| 263 |
| 264 /** |
| 265 * @override |
| 266 * @return {!Array<string>} |
| 267 */ |
| 268 capabilities: function() { |
| 269 return this._capabilities; |
| 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((function(service) { |
| 292 this.addOption(service); |
| 293 }).bind(this)); |
| 294 |
| 295 WebInspector.languageService.addEventListener(WebInspector.LanguageServiceMa
nager.Events.ServiceAdded, this._onServiceAdded, this); |
| 296 WebInspector.languageService.addEventListener(WebInspector.LanguageServiceMa
nager.Events.InferredMimeUpdated, this._onViewSelected, this); |
| 297 this._selectElement.addEventListener("change", this._languageServiceChanged.
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.data.serv
ice); |
| 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.I
nferred) { |
| 328 option.text = WebInspector.UIString("%s (inferred)", name); |
| 329 } |
| 330 } |
| 331 }, |
| 332 |
| 333 _languageServiceChanged: function() { |
| 334 var option = this._selectedOption(); |
| 335 switch(option.__type) { |
| 336 case WebInspector.LanguageServiceModel.OptionType.Inferred: |
| 337 this._capabilities.forEach(function(c) { |
| 338 WebInspector.languageService.enableContextualHandling(c); |
| 339 }); |
| 340 break; |
| 341 case WebInspector.LanguageServiceModel.OptionType.Builtin: |
| 342 this._capabilities.forEach(function(c) { |
| 343 WebInspector.languageService.disableContextualHandling(c); |
| 344 }); |
| 345 break; |
| 346 case WebInspector.LanguageServiceModel.OptionType.External: |
| 347 this._capabilities.forEach(function(c) { |
| 348 WebInspector.languageService.disableContextualHandling(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 |