| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 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 WebInspector.JavaScriptAutocomplete = {}; |
| 6 |
| 7 /** |
| 8 * @param {!Element} proxyElement |
| 9 * @param {!Range} wordRange |
| 10 * @param {boolean} force |
| 11 * @param {function(!Array.<string>, number=)} completionsReadyCallback |
| 12 */ |
| 13 WebInspector.JavaScriptAutocomplete.completionsForTextPromptInCurrentContext = f
unction(proxyElement, wordRange, force, completionsReadyCallback) { |
| 14 var expressionRange = wordRange.cloneRange(); |
| 15 expressionRange.collapse(true); |
| 16 expressionRange.setStartBefore(proxyElement); |
| 17 WebInspector.JavaScriptAutocomplete.completionsForTextInCurrentContext(express
ionRange.toString(), wordRange.toString(), force) |
| 18 .then(completionsReadyCallback); |
| 19 }; |
| 20 |
| 21 /** |
| 22 * @param {string} text |
| 23 * @param {string} completionsPrefix |
| 24 * @param {boolean=} force |
| 25 * @return {!Promise<!Array<string>>} |
| 26 */ |
| 27 WebInspector.JavaScriptAutocomplete.completionsForTextInCurrentContext = functio
n(text, completionsPrefix, force) { |
| 28 var index; |
| 29 var stopChars = new Set(' =:({;,!+-*/&|^<>`'.split('')); |
| 30 for (index = text.length - 1; index >= 0; index--) { |
| 31 // Pass less stop characters to rangeOfWord so the range will be a more comp
lete expression. |
| 32 if (stopChars.has(text.charAt(index))) |
| 33 break; |
| 34 } |
| 35 var clippedExpression = text.substring(index + 1); |
| 36 var bracketCount = 0; |
| 37 |
| 38 index = clippedExpression.length - 1; |
| 39 while (index >= 0) { |
| 40 var character = clippedExpression.charAt(index); |
| 41 if (character === ']') |
| 42 bracketCount++; |
| 43 // Allow an open bracket at the end for property completion. |
| 44 if (character === '[' && index < clippedExpression.length - 1) { |
| 45 bracketCount--; |
| 46 if (bracketCount < 0) |
| 47 break; |
| 48 } |
| 49 index--; |
| 50 } |
| 51 clippedExpression = clippedExpression.substring(index + 1); |
| 52 |
| 53 return WebInspector.JavaScriptAutocomplete.completionsForExpression(clippedExp
ression, completionsPrefix, force); |
| 54 }; |
| 55 |
| 56 |
| 57 /** |
| 58 * @param {string} expressionString |
| 59 * @param {string} prefix |
| 60 * @param {boolean=} force |
| 61 * @return {!Promise<!Array<string>>} |
| 62 */ |
| 63 WebInspector.JavaScriptAutocomplete.completionsForExpression = function(expressi
onString, prefix, force) { |
| 64 var executionContext = WebInspector.context.flavor(WebInspector.ExecutionConte
xt); |
| 65 if (!executionContext) |
| 66 return Promise.resolve([]); |
| 67 |
| 68 var lastIndex = expressionString.length - 1; |
| 69 |
| 70 var dotNotation = (expressionString[lastIndex] === '.'); |
| 71 var bracketNotation = (expressionString[lastIndex] === '['); |
| 72 |
| 73 if (dotNotation || bracketNotation) |
| 74 expressionString = expressionString.substr(0, lastIndex); |
| 75 |
| 76 // User is entering float value, do not suggest anything. |
| 77 if (expressionString && !isNaN(expressionString)) |
| 78 return Promise.resolve([]); |
| 79 |
| 80 if (!prefix && !expressionString && !force) |
| 81 return Promise.resolve([]); |
| 82 |
| 83 var fufill; |
| 84 var promise = new Promise(x => fufill = x); |
| 85 if (!expressionString && executionContext.debuggerModel.selectedCallFrame()) |
| 86 executionContext.debuggerModel.selectedCallFrame().variableNames(receivedPro
pertyNames); |
| 87 else |
| 88 executionContext.evaluate(expressionString, 'completion', true, true, false,
false, false, evaluated); |
| 89 |
| 90 return promise; |
| 91 /** |
| 92 * @param {?WebInspector.RemoteObject} result |
| 93 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails |
| 94 */ |
| 95 function evaluated(result, exceptionDetails) { |
| 96 if (!result || !!exceptionDetails) { |
| 97 fufill([]); |
| 98 return; |
| 99 } |
| 100 |
| 101 /** |
| 102 * @param {?WebInspector.RemoteObject} object |
| 103 * @return {!Promise<?WebInspector.RemoteObject>} |
| 104 */ |
| 105 function extractTarget(object) { |
| 106 if (!object) |
| 107 return Promise.resolve(/** @type {?WebInspector.RemoteObject} */(null)); |
| 108 if (object.type !== 'object' || object.subtype !== 'proxy') |
| 109 return Promise.resolve(/** @type {?WebInspector.RemoteObject} */(object)
); |
| 110 return object.getOwnPropertiesPromise().then(extractTargetFromProperties).
then(extractTarget); |
| 111 } |
| 112 |
| 113 /** |
| 114 * @param {!{properties: ?Array<!WebInspector.RemoteObjectProperty>, interna
lProperties: ?Array<!WebInspector.RemoteObjectProperty>}} properties |
| 115 * @return {?WebInspector.RemoteObject} |
| 116 */ |
| 117 function extractTargetFromProperties(properties) { |
| 118 var internalProperties = properties.internalProperties || []; |
| 119 var target = internalProperties.find(property => property.name === '[[Targ
et]]'); |
| 120 return target ? target.value : null; |
| 121 } |
| 122 |
| 123 /** |
| 124 * @param {string=} type |
| 125 * @return {!Object} |
| 126 * @suppressReceiverCheck |
| 127 * @this {Object} |
| 128 */ |
| 129 function getCompletions(type) { |
| 130 var object; |
| 131 if (type === 'string') |
| 132 object = new String(''); |
| 133 else if (type === 'number') |
| 134 object = new Number(0); |
| 135 else if (type === 'boolean') |
| 136 object = new Boolean(false); |
| 137 else |
| 138 object = this; |
| 139 |
| 140 var resultSet = { __proto__: null }; |
| 141 try { |
| 142 for (var o = object; o; o = Object.getPrototypeOf(o)) { |
| 143 if ((type === 'array' || type === 'typedarray') && o === object && Arr
ayBuffer.isView(o) && o.length > 9999) |
| 144 continue; |
| 145 var names = Object.getOwnPropertyNames(o); |
| 146 var isArray = Array.isArray(o); |
| 147 for (var i = 0; i < names.length; ++i) { |
| 148 // Skip array elements indexes. |
| 149 if (isArray && /^[0-9]/.test(names[i])) |
| 150 continue; |
| 151 resultSet[names[i]] = true; |
| 152 } |
| 153 } |
| 154 } catch (e) { |
| 155 } |
| 156 return resultSet; |
| 157 } |
| 158 |
| 159 /** |
| 160 * @param {?WebInspector.RemoteObject} object |
| 161 */ |
| 162 function completionsForObject(object) { |
| 163 if (!object) |
| 164 receivedPropertyNames(null); |
| 165 else if (object.type === 'object' || object.type === 'function') |
| 166 object.callFunctionJSON( |
| 167 getCompletions, [WebInspector.RemoteObject.toCallArgument(object.subty
pe)], |
| 168 receivedPropertyNames); |
| 169 else if (object.type === 'string' || object.type === 'number' || object.ty
pe === 'boolean') |
| 170 executionContext.evaluate( |
| 171 '(' + getCompletions + ')("' + result.type + '")', 'completion', false
, true, true, false, false, |
| 172 receivedPropertyNamesFromEval); |
| 173 } |
| 174 |
| 175 extractTarget(result).then(completionsForObject); |
| 176 } |
| 177 |
| 178 /** |
| 179 * @param {?WebInspector.RemoteObject} result |
| 180 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails |
| 181 */ |
| 182 function receivedPropertyNamesFromEval(result, exceptionDetails) { |
| 183 executionContext.target().runtimeAgent().releaseObjectGroup('completion'); |
| 184 if (result && !exceptionDetails) |
| 185 receivedPropertyNames(/** @type {!Object} */(result.value)); |
| 186 else |
| 187 fufill([]); |
| 188 } |
| 189 |
| 190 /** |
| 191 * @param {?Object} propertyNames |
| 192 */ |
| 193 function receivedPropertyNames(propertyNames) { |
| 194 executionContext.target().runtimeAgent().releaseObjectGroup('completion'); |
| 195 if (!propertyNames) { |
| 196 fufill([]); |
| 197 return; |
| 198 } |
| 199 var includeCommandLineAPI = (!dotNotation && !bracketNotation); |
| 200 if (includeCommandLineAPI) { |
| 201 const commandLineAPI = [ |
| 202 'dir', |
| 203 'dirxml', |
| 204 'keys', |
| 205 'values', |
| 206 'profile', |
| 207 'profileEnd', |
| 208 'monitorEvents', |
| 209 'unmonitorEvents', |
| 210 'inspect', |
| 211 'copy', |
| 212 'clear', |
| 213 'getEventListeners', |
| 214 'debug', |
| 215 'undebug', |
| 216 'monitor', |
| 217 'unmonitor', |
| 218 'table', |
| 219 '$', |
| 220 '$$', |
| 221 '$x' |
| 222 ]; |
| 223 for (var i = 0; i < commandLineAPI.length; ++i) |
| 224 propertyNames[commandLineAPI[i]] = true; |
| 225 } |
| 226 fufill(WebInspector.JavaScriptAutocomplete._completionsForPrefix( |
| 227 dotNotation, bracketNotation, expressionString, prefix, Object.keys(proper
tyNames))); |
| 228 } |
| 229 }; |
| 230 |
| 231 /** |
| 232 * @param {boolean} dotNotation |
| 233 * @param {boolean} bracketNotation |
| 234 * @param {string} expressionString |
| 235 * @param {string} prefix |
| 236 * @param {!Array.<string>} properties |
| 237 * @return {!Array<string>} |
| 238 */ |
| 239 WebInspector.JavaScriptAutocomplete._completionsForPrefix = function(dotNotation
, bracketNotation, expressionString, prefix, properties) { |
| 240 if (bracketNotation) { |
| 241 if (prefix.length && prefix[0] === '\'') |
| 242 var quoteUsed = '\''; |
| 243 else |
| 244 var quoteUsed = '"'; |
| 245 } |
| 246 |
| 247 var results = []; |
| 248 |
| 249 if (!expressionString) { |
| 250 const keywords = [ |
| 251 'break', 'case', 'catch', 'continue', 'default', 'delete', 'do', 'else', '
finally', |
| 252 'for', 'function', 'if', 'in', 'instanceof', 'new', 'return', 'switch', 't
his', |
| 253 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with' |
| 254 ]; |
| 255 properties = properties.concat(keywords); |
| 256 } |
| 257 |
| 258 properties.sort(); |
| 259 |
| 260 for (var i = 0; i < properties.length; ++i) { |
| 261 var property = properties[i]; |
| 262 |
| 263 // Assume that all non-ASCII characters are letters and thus can be used as
part of identifier. |
| 264 if (dotNotation && !/^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFFF]*$/.t
est(property)) |
| 265 continue; |
| 266 |
| 267 if (bracketNotation) { |
| 268 if (!/^[0-9]+$/.test(property)) |
| 269 property = quoteUsed + property.escapeCharacters(quoteUsed + '\\') + quo
teUsed; |
| 270 property += ']'; |
| 271 } |
| 272 |
| 273 if (property.length < prefix.length) |
| 274 continue; |
| 275 if (prefix.length && !property.startsWith(prefix)) |
| 276 continue; |
| 277 |
| 278 // Substitute actual newlines with newline characters. @see crbug.com/498421 |
| 279 results.push(property.split('\n').join('\\n')); |
| 280 } |
| 281 return results; |
| 282 }; |
| OLD | NEW |