OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 Components.JavaScriptAutocomplete = {}; | 5 Components.JavaScriptAutocomplete = {}; |
6 | 6 |
7 /** @typedef {{title:(string|undefined), items:Array<string>}} */ | 7 /** @typedef {{title:(string|undefined), items:Array<string>}} */ |
8 Components.JavaScriptAutocomplete.CompletionGroup; | 8 Components.JavaScriptAutocomplete.CompletionGroup; |
9 | 9 |
10 /** | 10 /** |
11 * @param {string} text | 11 * @param {string} text |
12 * @param {string} query | 12 * @param {string} query |
13 * @param {boolean=} force | 13 * @param {boolean=} force |
14 * @return {!Promise<!UI.SuggestBox.Suggestions>} | 14 * @return {!Promise<!UI.SuggestBox.Suggestions>} |
15 */ | 15 */ |
16 Components.JavaScriptAutocomplete.completionsForTextInCurrentContext = function( text, query, force) { | 16 Components.JavaScriptAutocomplete.completionsForTextInCurrentContext = function( text, query, force) { |
17 var clippedExpression = Components.JavaScriptAutocomplete._clipExpression(text , true); | |
18 var mapCompletionsPromise = Components.JavaScriptAutocomplete._mapCompletions( text, query); | |
19 return Components.JavaScriptAutocomplete.completionsForExpression(clippedExpre ssion, query, force) | |
20 .then(completions => mapCompletionsPromise.then(mapCompletions => mapCompl etions.concat(completions))); | |
21 }; | |
22 | |
23 /** | |
24 * @param {string} text | |
25 * @param {boolean=} allowEndingBracket | |
26 * @return {string} | |
27 */ | |
28 Components.JavaScriptAutocomplete._clipExpression = function(text, allowEndingBr acket) { | |
17 var index; | 29 var index; |
18 var stopChars = new Set('=:({;,!+-*/&|^<>`'.split('')); | 30 var stopChars = new Set('=:({;,!+-*/&|^<>`'.split('')); |
19 var whiteSpaceChars = new Set(' \r\n\t'.split('')); | 31 var whiteSpaceChars = new Set(' \r\n\t'.split('')); |
20 var continueChars = new Set('[. \r\n\t'.split('')); | 32 var continueChars = new Set('[. \r\n\t'.split('')); |
21 | 33 |
22 for (index = text.length - 1; index >= 0; index--) { | 34 for (index = text.length - 1; index >= 0; index--) { |
23 // Pass less stop characters to rangeOfWord so the range will be a more comp lete expression. | |
24 if (stopChars.has(text.charAt(index))) | 35 if (stopChars.has(text.charAt(index))) |
25 break; | 36 break; |
26 if (whiteSpaceChars.has(text.charAt(index)) && !continueChars.has(text.charA t(index - 1))) | 37 if (whiteSpaceChars.has(text.charAt(index)) && !continueChars.has(text.charA t(index - 1))) |
27 break; | 38 break; |
28 } | 39 } |
29 var clippedExpression = text.substring(index + 1).trim(); | 40 var clippedExpression = text.substring(index + 1).trim(); |
30 var bracketCount = 0; | 41 var bracketCount = 0; |
31 | 42 |
32 index = clippedExpression.length - 1; | 43 index = clippedExpression.length - 1; |
33 while (index >= 0) { | 44 while (index >= 0) { |
34 var character = clippedExpression.charAt(index); | 45 var character = clippedExpression.charAt(index); |
35 if (character === ']') | 46 if (character === ']') |
36 bracketCount++; | 47 bracketCount++; |
37 // Allow an open bracket at the end for property completion. | 48 // Allow an open bracket at the end for property completion. |
38 if (character === '[' && index < clippedExpression.length - 1) { | 49 if (character === '[' && (index < clippedExpression.length - 1 || !allowEndi ngBracket)) { |
39 bracketCount--; | 50 bracketCount--; |
40 if (bracketCount < 0) | 51 if (bracketCount < 0) |
41 break; | 52 break; |
42 } | 53 } |
43 index--; | 54 index--; |
44 } | 55 } |
45 clippedExpression = clippedExpression.substring(index + 1).trim(); | 56 return clippedExpression.substring(index + 1).trim(); |
46 | |
47 return Components.JavaScriptAutocomplete.completionsForExpression(clippedExpre ssion, query, force); | |
48 }; | 57 }; |
49 | 58 |
50 /** | 59 /** |
60 * @param {string} text | |
61 * @param {string} query | |
62 * @return {!Promise<!UI.SuggestBox.Suggestions>} | |
63 */ | |
64 Components.JavaScriptAutocomplete._mapCompletions = function(text, query) { | |
65 var mapMatch = text.match(/\.\s*(get|set|delete)\s*\(\s*$/); | |
66 var executionContext = UI.context.flavor(SDK.ExecutionContext); | |
67 if (!executionContext || !mapMatch) | |
68 return Promise.resolve([]); | |
69 | |
70 var clippedExpression = Components.JavaScriptAutocomplete._clipExpression(text .substring(0, mapMatch.index)); | |
71 var fulfill; | |
72 var promise = new Promise(x => fulfill = x); | |
73 executionContext.evaluate(clippedExpression, 'completion', true, true, false, false, false, evaluated); | |
74 return promise; | |
75 | |
76 /** | |
77 * @param {?SDK.RemoteObject} result | |
78 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails | |
79 */ | |
80 function evaluated(result, exceptionDetails) { | |
81 if (!result || !!exceptionDetails || result.subtype !== 'map') { | |
82 fulfill([]); | |
83 return; | |
84 } | |
85 result.getOwnPropertiesPromise().then(extractEntriesProperty); | |
86 } | |
87 | |
88 /** | |
89 * @param {!{properties: ?Array<!SDK.RemoteObjectProperty>, internalProperties : ?Array<!SDK.RemoteObjectProperty>}} properties | |
90 */ | |
91 function extractEntriesProperty(properties) { | |
92 var internalProperties = properties.internalProperties || []; | |
93 var entriesProperty = internalProperties.find(property => property.name === '[[Entries]]'); | |
94 if (!entriesProperty) { | |
95 fulfill([]); | |
96 return; | |
97 } | |
98 entriesProperty.value.callFunctionJSONPromise(getEntries).then(keysObj => go tKeys(Object.keys(keysObj))); | |
99 } | |
100 | |
101 /** | |
102 * @suppressReceiverCheck | |
103 * @this {!Array<{key:?, value:?}>} | |
104 * @return {!Object} | |
105 */ | |
106 function getEntries() { | |
107 var result = {__proto__: null}; | |
108 for (var i = 0; i < this.length; i++) { | |
109 if (typeof this[i].key === 'string') | |
110 result[this[i].key] = true; | |
111 } | |
112 return result; | |
113 } | |
114 | |
115 /** | |
116 * @param {!Array<string>} rawKeys | |
117 */ | |
118 function gotKeys(rawKeys) { | |
119 var caseSensitivePrefix = []; | |
120 var caseInsensitivePrefix = []; | |
121 var caseSensitiveAnywhere = []; | |
122 var caseInsensitiveAnywhere = []; | |
123 var quoteChar = '"'; | |
124 if (query.startsWith('\'')) | |
125 quoteChar = '\''; | |
126 var endChar = ')'; | |
127 if (mapMatch[0].indexOf('set') !== -1) | |
128 endChar = ', '; | |
129 | |
130 var keys = rawKeys.sort(String.naturalOrderComparator).map(key => quoteChar + key + quoteChar + endChar); | |
dgozman
2017/01/23 23:57:49
Let's not use naturalOrderComparator for large arr
einbinder
2017/01/24 19:06:32
Done.
| |
131 | |
132 for (var key of keys) { | |
133 if (key.length < query.length) | |
134 continue; | |
135 if (query.length && key.toLowerCase().indexOf(query.toLowerCase()) === -1) | |
136 continue; | |
137 // Substitute actual newlines with newline characters. @see crbug.com/4984 21 | |
138 var title = key.split('\n').join('\\n'); | |
139 | |
140 if (key.startsWith(query)) | |
141 caseSensitivePrefix.push({title: title, priority: 4}); | |
142 else if (key.toLowerCase().startsWith(query.toLowerCase())) | |
143 caseInsensitivePrefix.push({title: title, priority: 3}); | |
144 else if (key.indexOf(query) !== -1) | |
145 caseSensitiveAnywhere.push({title: title, priority: 2}); | |
146 else | |
147 caseInsensitiveAnywhere.push({title: title, priority: 1}); | |
148 } | |
149 var suggestions = caseSensitivePrefix.concat(caseInsensitivePrefix, caseSens itiveAnywhere, caseInsensitiveAnywhere); | |
150 if (suggestions.length) | |
151 suggestions[0].subtitle = Common.UIString('Keys'); | |
152 fulfill(suggestions); | |
153 } | |
154 }; | |
155 | |
156 /** | |
51 * @param {string} expressionString | 157 * @param {string} expressionString |
52 * @param {string} query | 158 * @param {string} query |
53 * @param {boolean=} force | 159 * @param {boolean=} force |
54 * @return {!Promise<!UI.SuggestBox.Suggestions>} | 160 * @return {!Promise<!UI.SuggestBox.Suggestions>} |
55 */ | 161 */ |
56 Components.JavaScriptAutocomplete.completionsForExpression = function(expression String, query, force) { | 162 Components.JavaScriptAutocomplete.completionsForExpression = function(expression String, query, force) { |
57 var executionContext = UI.context.flavor(SDK.ExecutionContext); | 163 var executionContext = UI.context.flavor(SDK.ExecutionContext); |
58 if (!executionContext) | 164 if (!executionContext) |
59 return Promise.resolve([]); | 165 return Promise.resolve([]); |
60 | 166 |
61 var lastIndex = expressionString.length - 1; | 167 var lastIndex = expressionString.length - 1; |
62 | 168 |
63 var dotNotation = (expressionString[lastIndex] === '.'); | 169 var dotNotation = (expressionString[lastIndex] === '.'); |
64 var bracketNotation = (expressionString.length > 1 && expressionString[lastInd ex] === '['); | 170 var bracketNotation = (expressionString.length > 1 && expressionString[lastInd ex] === '['); |
65 | 171 |
66 if (dotNotation || bracketNotation) | 172 if (dotNotation || bracketNotation) |
67 expressionString = expressionString.substr(0, lastIndex); | 173 expressionString = expressionString.substr(0, lastIndex); |
68 else | 174 else |
69 expressionString = ''; | 175 expressionString = ''; |
70 | 176 |
71 // User is entering float value, do not suggest anything. | 177 // User is entering float value, do not suggest anything. |
72 if ((expressionString && !isNaN(expressionString)) || (!expressionString && qu ery && !isNaN(query))) | 178 if ((expressionString && !isNaN(expressionString)) || (!expressionString && qu ery && !isNaN(query))) |
73 return Promise.resolve([]); | 179 return Promise.resolve([]); |
74 | 180 |
75 | 181 |
76 if (!query && !expressionString && !force) | 182 if (!query && !expressionString && !force) |
77 return Promise.resolve([]); | 183 return Promise.resolve([]); |
78 | 184 |
79 var fufill; | 185 var fulfill; |
80 var promise = new Promise(x => fufill = x); | 186 var promise = new Promise(x => fulfill = x); |
81 var selectedFrame = executionContext.debuggerModel.selectedCallFrame(); | 187 var selectedFrame = executionContext.debuggerModel.selectedCallFrame(); |
82 if (!expressionString && selectedFrame) | 188 if (!expressionString && selectedFrame) |
83 variableNamesInScopes(selectedFrame, receivedPropertyNames); | 189 variableNamesInScopes(selectedFrame, receivedPropertyNames); |
84 else | 190 else |
85 executionContext.evaluate(expressionString, 'completion', true, true, false, false, false, evaluated); | 191 executionContext.evaluate(expressionString, 'completion', true, true, false, false, false, evaluated); |
86 | 192 |
87 return promise; | 193 return promise; |
88 /** | 194 /** |
89 * @param {?SDK.RemoteObject} result | 195 * @param {?SDK.RemoteObject} result |
90 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails | 196 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails |
91 */ | 197 */ |
92 function evaluated(result, exceptionDetails) { | 198 function evaluated(result, exceptionDetails) { |
93 if (!result || !!exceptionDetails) { | 199 if (!result || !!exceptionDetails) { |
94 fufill([]); | 200 fulfill([]); |
95 return; | 201 return; |
96 } | 202 } |
97 | 203 |
98 /** | 204 /** |
99 * @param {?SDK.RemoteObject} object | 205 * @param {?SDK.RemoteObject} object |
100 * @return {!Promise<?SDK.RemoteObject>} | 206 * @return {!Promise<?SDK.RemoteObject>} |
101 */ | 207 */ |
102 function extractTarget(object) { | 208 function extractTarget(object) { |
103 if (!object) | 209 if (!object) |
104 return Promise.resolve(/** @type {?SDK.RemoteObject} */ (null)); | 210 return Promise.resolve(/** @type {?SDK.RemoteObject} */ (null)); |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
212 | 318 |
213 /** | 319 /** |
214 * @param {?SDK.RemoteObject} result | 320 * @param {?SDK.RemoteObject} result |
215 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails | 321 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails |
216 */ | 322 */ |
217 function receivedPropertyNamesFromEval(result, exceptionDetails) { | 323 function receivedPropertyNamesFromEval(result, exceptionDetails) { |
218 executionContext.target().runtimeAgent().releaseObjectGroup('completion'); | 324 executionContext.target().runtimeAgent().releaseObjectGroup('completion'); |
219 if (result && !exceptionDetails) | 325 if (result && !exceptionDetails) |
220 receivedPropertyNames(/** @type {!Object} */ (result.value)); | 326 receivedPropertyNames(/** @type {!Object} */ (result.value)); |
221 else | 327 else |
222 fufill([]); | 328 fulfill([]); |
223 } | 329 } |
224 | 330 |
225 /** | 331 /** |
226 * @param {?Object} object | 332 * @param {?Object} object |
227 */ | 333 */ |
228 function receivedPropertyNames(object) { | 334 function receivedPropertyNames(object) { |
229 executionContext.target().runtimeAgent().releaseObjectGroup('completion'); | 335 executionContext.target().runtimeAgent().releaseObjectGroup('completion'); |
230 if (!object) { | 336 if (!object) { |
231 fufill([]); | 337 fulfill([]); |
232 return; | 338 return; |
233 } | 339 } |
234 var propertyGroups = /** @type {!Array<!Components.JavaScriptAutocomplete.Co mpletionGroup>} */ (object); | 340 var propertyGroups = /** @type {!Array<!Components.JavaScriptAutocomplete.Co mpletionGroup>} */ (object); |
235 var includeCommandLineAPI = (!dotNotation && !bracketNotation); | 341 var includeCommandLineAPI = (!dotNotation && !bracketNotation); |
236 if (includeCommandLineAPI) { | 342 if (includeCommandLineAPI) { |
237 const commandLineAPI = [ | 343 const commandLineAPI = [ |
238 'dir', | 344 'dir', |
239 'dirxml', | 345 'dirxml', |
240 'keys', | 346 'keys', |
241 'values', | 347 'values', |
242 'profile', | 348 'profile', |
243 'profileEnd', | 349 'profileEnd', |
244 'monitorEvents', | 350 'monitorEvents', |
245 'unmonitorEvents', | 351 'unmonitorEvents', |
246 'inspect', | 352 'inspect', |
247 'copy', | 353 'copy', |
248 'clear', | 354 'clear', |
249 'getEventListeners', | 355 'getEventListeners', |
250 'debug', | 356 'debug', |
251 'undebug', | 357 'undebug', |
252 'monitor', | 358 'monitor', |
253 'unmonitor', | 359 'unmonitor', |
254 'table', | 360 'table', |
255 '$', | 361 '$', |
256 '$$', | 362 '$$', |
257 '$x' | 363 '$x' |
258 ]; | 364 ]; |
259 propertyGroups.push({items: commandLineAPI}); | 365 propertyGroups.push({items: commandLineAPI}); |
260 } | 366 } |
261 fufill(Components.JavaScriptAutocomplete._completionsForQuery( | 367 fulfill(Components.JavaScriptAutocomplete._completionsForQuery( |
262 dotNotation, bracketNotation, expressionString, query, propertyGroups)); | 368 dotNotation, bracketNotation, expressionString, query, propertyGroups)); |
263 } | 369 } |
264 }; | 370 }; |
265 | 371 |
266 /** | 372 /** |
267 * @param {boolean} dotNotation | 373 * @param {boolean} dotNotation |
268 * @param {boolean} bracketNotation | 374 * @param {boolean} bracketNotation |
269 * @param {string} expressionString | 375 * @param {string} expressionString |
270 * @param {string} query | 376 * @param {string} query |
271 * @param {!Array<!Components.JavaScriptAutocomplete.CompletionGroup>} propert yGroups | 377 * @param {!Array<!Components.JavaScriptAutocomplete.CompletionGroup>} propert yGroups |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
343 function itemComparator(a, b) { | 449 function itemComparator(a, b) { |
344 var aStartsWithUnderscore = a.startsWith('_'); | 450 var aStartsWithUnderscore = a.startsWith('_'); |
345 var bStartsWithUnderscore = b.startsWith('_'); | 451 var bStartsWithUnderscore = b.startsWith('_'); |
346 if (aStartsWithUnderscore && !bStartsWithUnderscore) | 452 if (aStartsWithUnderscore && !bStartsWithUnderscore) |
347 return 1; | 453 return 1; |
348 if (bStartsWithUnderscore && !aStartsWithUnderscore) | 454 if (bStartsWithUnderscore && !aStartsWithUnderscore) |
349 return -1; | 455 return -1; |
350 return String.naturalOrderComparator(a, b); | 456 return String.naturalOrderComparator(a, b); |
351 } | 457 } |
352 }; | 458 }; |
OLD | NEW |