Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js

Issue 2639703002: DevTools: Console: Provide autocompletions for Maps (Closed)
Patch Set: Add delete Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698