Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js | 
| diff --git a/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js b/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js | 
| index b9aad4f0374aa194fbc76020491617051507f0b3..b56b4309b02f925f33e0086bc3676db3d1a611c9 100644 | 
| --- a/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js | 
| +++ b/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js | 
| @@ -14,13 +14,24 @@ Components.JavaScriptAutocomplete.CompletionGroup; | 
| * @return {!Promise<!UI.SuggestBox.Suggestions>} | 
| */ | 
| Components.JavaScriptAutocomplete.completionsForTextInCurrentContext = function(text, query, force) { | 
| + var clippedExpression = Components.JavaScriptAutocomplete._clipExpression(text, true); | 
| + var mapCompletionsPromise = Components.JavaScriptAutocomplete._mapCompletions(text, query); | 
| + return Components.JavaScriptAutocomplete.completionsForExpression(clippedExpression, query, force) | 
| + .then(completions => mapCompletionsPromise.then(mapCompletions => mapCompletions.concat(completions))); | 
| +}; | 
| + | 
| +/** | 
| + * @param {string} text | 
| + * @param {boolean=} allowEndingBracket | 
| + * @return {string} | 
| + */ | 
| +Components.JavaScriptAutocomplete._clipExpression = function(text, allowEndingBracket) { | 
| var index; | 
| var stopChars = new Set('=:({;,!+-*/&|^<>`'.split('')); | 
| var whiteSpaceChars = new Set(' \r\n\t'.split('')); | 
| var continueChars = new Set('[. \r\n\t'.split('')); | 
| for (index = text.length - 1; index >= 0; index--) { | 
| - // Pass less stop characters to rangeOfWord so the range will be a more complete expression. | 
| if (stopChars.has(text.charAt(index))) | 
| break; | 
| if (whiteSpaceChars.has(text.charAt(index)) && !continueChars.has(text.charAt(index - 1))) | 
| @@ -35,16 +46,110 @@ Components.JavaScriptAutocomplete.completionsForTextInCurrentContext = function( | 
| if (character === ']') | 
| bracketCount++; | 
| // Allow an open bracket at the end for property completion. | 
| - if (character === '[' && index < clippedExpression.length - 1) { | 
| + if (character === '[' && (index < clippedExpression.length - 1 || !allowEndingBracket)) { | 
| bracketCount--; | 
| if (bracketCount < 0) | 
| break; | 
| } | 
| index--; | 
| } | 
| - clippedExpression = clippedExpression.substring(index + 1).trim(); | 
| + return clippedExpression.substring(index + 1).trim(); | 
| +}; | 
| + | 
| +/** | 
| + * @param {string} text | 
| + * @param {string} query | 
| + * @return {!Promise<!UI.SuggestBox.Suggestions>} | 
| + */ | 
| +Components.JavaScriptAutocomplete._mapCompletions = function(text, query) { | 
| + var mapMatch = text.match(/\.\s*[gs]et\s*\(\s*$/); | 
| + var executionContext = UI.context.flavor(SDK.ExecutionContext); | 
| + if (!executionContext || !mapMatch) | 
| + return Promise.resolve([]); | 
| + | 
| + var clippedExpression = Components.JavaScriptAutocomplete._clipExpression(text.substring(0, mapMatch.index), true); | 
| + var fufill; | 
| + var promise = new Promise(x => fufill = x); | 
| + executionContext.evaluate(clippedExpression, 'completion', true, true, false, false, false, evaluated); | 
| + return promise; | 
| + | 
| + /** | 
| + * @param {?SDK.RemoteObject} result | 
| + * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails | 
| + */ | 
| + function evaluated(result, exceptionDetails) { | 
| + if (!result || !!exceptionDetails || result.subtype !== 'map') { | 
| + fufill([]); | 
| + return; | 
| + } | 
| + result.getOwnPropertiesPromise().then(extractEntriesProperty); | 
| 
 
luoe
2017/01/19 18:54:21
Can we make this a method in RemoteObject.js as ge
 
einbinder
2017/01/19 20:21:38
This only gets the entries from maps with keys as
 
 | 
| + } | 
| + | 
| + /** | 
| + * @param {!{properties: ?Array<!SDK.RemoteObjectProperty>, internalProperties: ?Array<!SDK.RemoteObjectProperty>}} properties | 
| + */ | 
| + function extractEntriesProperty(properties) { | 
| + var internalProperties = properties.internalProperties || []; | 
| + var entriesProperty = internalProperties.find(property => property.name === '[[Entries]]'); | 
| + if (!entriesProperty) { | 
| + fufill([]); | 
| + return; | 
| + } | 
| + entriesProperty.value.callFunctionJSONPromise(getEntries).then(keysObj => gotKeys(Object.keys(keysObj))); | 
| + } | 
| - return Components.JavaScriptAutocomplete.completionsForExpression(clippedExpression, query, force); | 
| + /** | 
| + * @suppressReceiverCheck | 
| + * @this {!Array<{key:?, value:?}>} | 
| 
 
luoe
2017/01/19 18:54:20
@return ?
 
einbinder
2017/01/19 20:21:38
Done.
 
 | 
| + */ | 
| + function getEntries() { | 
| + var result = {__proto__: null}; | 
| + for (var i = 0; i < this.length; i++) { | 
| + if (typeof this[i].key === 'string') | 
| + result[this[i].key] = true; | 
| + } | 
| + return result; | 
| + } | 
| + | 
| + /** | 
| + * @param {!Array<string>} rawKeys | 
| + */ | 
| + function gotKeys(rawKeys) { | 
| + var caseSensitivePrefix = []; | 
| + var caseInsensitivePrefix = []; | 
| + var caseSensitiveAnywhere = []; | 
| + var caseInsensitiveAnywhere = []; | 
| + var qouteChar = '"'; | 
| 
 
luoe
2017/01/19 18:54:20
nit: quoteChar
 
einbinder
2017/01/19 20:21:38
Done.
 
 | 
| + if (query.startsWith('\'')) | 
| + qouteChar = '\''; | 
| + var endChar = ')'; | 
| + if (mapMatch[0].indexOf('s') !== -1) | 
| + endChar = ', '; | 
| + | 
| + var keys = rawKeys.sort(String.naturalOrderComparator).map(key => qouteChar + key + qouteChar + endChar); | 
| + | 
| + for (var key of keys) { | 
| + if (key.length < query.length) | 
| + continue; | 
| + if (query.length && key.toLowerCase().indexOf(query.toLowerCase()) === -1) | 
| + continue; | 
| + // Substitute actual newlines with newline characters. @see crbug.com/498421 | 
| + var title = key.split('\n').join('\\n'); | 
| + | 
| + if (key.startsWith(query)) | 
| + caseSensitivePrefix.push({title: title, priority: 4}); | 
| + else if (key.toLowerCase().startsWith(query.toLowerCase())) | 
| + caseInsensitivePrefix.push({title: title, priority: 3}); | 
| + else if (key.indexOf(query) !== -1) | 
| + caseSensitiveAnywhere.push({title: title, priority: 2}); | 
| + else | 
| + caseInsensitiveAnywhere.push({title: title, priority: 1}); | 
| + } | 
| + var suggestions = caseSensitivePrefix.concat(caseInsensitivePrefix, caseSensitiveAnywhere, caseInsensitiveAnywhere); | 
| + if (suggestions.length) | 
| + suggestions[0].subtitle = 'Keys'; | 
| + fufill(suggestions); | 
| + } | 
| }; | 
| /** |