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

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

Issue 2557763003: DevTools: sort completions by prototype. (Closed)
Patch Set: rebaselined Created 4 years 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>}} */
8 Components.JavaScriptAutocomplete.CompletionGroup;
9
7 /** 10 /**
8 * @param {string} text 11 * @param {string} text
9 * @param {string} query 12 * @param {string} query
10 * @param {boolean=} force 13 * @param {boolean=} force
11 * @return {!Promise<!UI.SuggestBox.Suggestions>} 14 * @return {!Promise<!UI.SuggestBox.Suggestions>}
12 */ 15 */
13 Components.JavaScriptAutocomplete.completionsForTextInCurrentContext = function( text, query, force) { 16 Components.JavaScriptAutocomplete.completionsForTextInCurrentContext = function( text, query, force) {
14 var index; 17 var index;
15 var stopChars = new Set(' =:({;,!+-*/&|^<>`'.split('')); 18 var stopChars = new Set(' =:({;,!+-*/&|^<>`'.split(''));
16 for (index = text.length - 1; index >= 0; index--) { 19 for (index = text.length - 1; index >= 0; index--) {
(...skipping 15 matching lines...) Expand all
32 if (bracketCount < 0) 35 if (bracketCount < 0)
33 break; 36 break;
34 } 37 }
35 index--; 38 index--;
36 } 39 }
37 clippedExpression = clippedExpression.substring(index + 1); 40 clippedExpression = clippedExpression.substring(index + 1);
38 41
39 return Components.JavaScriptAutocomplete.completionsForExpression(clippedExpre ssion, query, force); 42 return Components.JavaScriptAutocomplete.completionsForExpression(clippedExpre ssion, query, force);
40 }; 43 };
41 44
42
43 /** 45 /**
44 * @param {string} expressionString 46 * @param {string} expressionString
45 * @param {string} query 47 * @param {string} query
46 * @param {boolean=} force 48 * @param {boolean=} force
47 * @return {!Promise<!UI.SuggestBox.Suggestions>} 49 * @return {!Promise<!UI.SuggestBox.Suggestions>}
48 */ 50 */
49 Components.JavaScriptAutocomplete.completionsForExpression = function(expression String, query, force) { 51 Components.JavaScriptAutocomplete.completionsForExpression = function(expression String, query, force) {
50 var executionContext = UI.context.flavor(SDK.ExecutionContext); 52 var executionContext = UI.context.flavor(SDK.ExecutionContext);
51 if (!executionContext) 53 if (!executionContext)
52 return Promise.resolve([]); 54 return Promise.resolve([]);
53 55
54 var lastIndex = expressionString.length - 1; 56 var lastIndex = expressionString.length - 1;
55 57
56 var dotNotation = (expressionString[lastIndex] === '.'); 58 var dotNotation = (expressionString[lastIndex] === '.');
57 var bracketNotation = (expressionString[lastIndex] === '['); 59 var bracketNotation = (expressionString[lastIndex] === '[');
58 60
59 if (dotNotation || bracketNotation) 61 if (dotNotation || bracketNotation)
60 expressionString = expressionString.substr(0, lastIndex); 62 expressionString = expressionString.substr(0, lastIndex);
61 else 63 else
62 expressionString = ''; 64 expressionString = '';
63 65
64 // User is entering float value, do not suggest anything. 66 // User is entering float value, do not suggest anything.
65 if ((expressionString && !isNaN(expressionString)) || (!expressionString && qu ery && !isNaN(query))) 67 if ((expressionString && !isNaN(expressionString)) || (!expressionString && qu ery && !isNaN(query)))
66 return Promise.resolve([]); 68 return Promise.resolve([]);
67 69
68 // User is creating an array, do not suggest anything. 70 // User is creating an array, do not suggest anything.
69 if (bracketNotation && !expressionString) 71 if (bracketNotation && !expressionString)
70 return Promise.resolve([]); 72 return Promise.resolve([]);
71 73
72 if (!query && !expressionString && !force) 74 if (!query && !expressionString && !force)
73 return Promise.resolve([]); 75 return Promise.resolve([]);
74 76
75 var fufill; 77 var fufill;
76 var promise = new Promise(x => fufill = x); 78 var promise = new Promise(x => fufill = x);
77 if (!expressionString && executionContext.debuggerModel.selectedCallFrame()) 79 if (!expressionString && executionContext.debuggerModel.selectedCallFrame())
78 executionContext.debuggerModel.selectedCallFrame().variableNames(receivedPro pertyNames); 80 variableNamesInScopes(executionContext.debuggerModel.selectedCallFrame(), re ceivedPropertyNames);
79 else 81 else
80 executionContext.evaluate(expressionString, 'completion', true, true, false, false, false, evaluated); 82 executionContext.evaluate(expressionString, 'completion', true, true, false, false, false, evaluated);
81 83
82 return promise; 84 return promise;
83 /** 85 /**
84 * @param {?SDK.RemoteObject} result 86 * @param {?SDK.RemoteObject} result
85 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails 87 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails
86 */ 88 */
87 function evaluated(result, exceptionDetails) { 89 function evaluated(result, exceptionDetails) {
88 if (!result || !!exceptionDetails) { 90 if (!result || !!exceptionDetails) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 var object; 124 var object;
123 if (type === 'string') 125 if (type === 'string')
124 object = new String(''); 126 object = new String('');
125 else if (type === 'number') 127 else if (type === 'number')
126 object = new Number(0); 128 object = new Number(0);
127 else if (type === 'boolean') 129 else if (type === 'boolean')
128 object = new Boolean(false); 130 object = new Boolean(false);
129 else 131 else
130 object = this; 132 object = this;
131 133
132 var resultSet = {__proto__: null}; 134 var result = [];
133 try { 135 try {
134 for (var o = object; o; o = Object.getPrototypeOf(o)) { 136 for (var o = object; o; o = Object.getPrototypeOf(o)) {
135 if ((type === 'array' || type === 'typedarray') && o === object && Arr ayBuffer.isView(o) && o.length > 9999) 137 if ((type === 'array' || type === 'typedarray') && o === object && Arr ayBuffer.isView(o) && o.length > 9999)
136 continue; 138 continue;
139
140 var group = {items: [], __proto__: null};
141 try {
142 if (typeof o === 'object' && o.constructor && o.constructor.name)
143 group.title = o.constructor.name;
144 } catch (ee) {
145 // we could break upon cross origin check.
146 }
147 result[result.length] = group;
137 var names = Object.getOwnPropertyNames(o); 148 var names = Object.getOwnPropertyNames(o);
138 var isArray = Array.isArray(o); 149 var isArray = Array.isArray(o);
139 for (var i = 0; i < names.length; ++i) { 150 for (var i = 0; i < names.length; ++i) {
140 // Skip array elements indexes. 151 // Skip array elements indexes.
141 if (isArray && /^[0-9]/.test(names[i])) 152 if (isArray && /^[0-9]/.test(names[i]))
142 continue; 153 continue;
143 resultSet[names[i]] = true; 154 group.items[group.items.length] = names[i];
144 } 155 }
145 } 156 }
146 } catch (e) { 157 } catch (e) {
147 } 158 }
148 return resultSet; 159 return result;
149 } 160 }
150 161
151 /** 162 /**
152 * @param {?SDK.RemoteObject} object 163 * @param {?SDK.RemoteObject} object
153 */ 164 */
154 function completionsForObject(object) { 165 function completionsForObject(object) {
155 if (!object) { 166 if (!object) {
156 receivedPropertyNames(null); 167 receivedPropertyNames(null);
157 } else if (object.type === 'object' || object.type === 'function') { 168 } else if (object.type === 'object' || object.type === 'function') {
158 object.callFunctionJSON( 169 object.callFunctionJSON(
159 getCompletions, [SDK.RemoteObject.toCallArgument(object.subtype)], r eceivedPropertyNames); 170 getCompletions, [SDK.RemoteObject.toCallArgument(object.subtype)], r eceivedPropertyNames);
160 } else if (object.type === 'string' || object.type === 'number' || object. type === 'boolean') { 171 } else if (object.type === 'string' || object.type === 'number' || object. type === 'boolean') {
161 executionContext.evaluate( 172 executionContext.evaluate(
162 '(' + getCompletions + ')("' + result.type + '")', 'completion', fal se, true, true, false, false, 173 '(' + getCompletions + ')("' + result.type + '")', 'completion', fal se, true, true, false, false,
163 receivedPropertyNamesFromEval); 174 receivedPropertyNamesFromEval);
164 } 175 }
165 } 176 }
166 177
167 extractTarget(result).then(completionsForObject); 178 extractTarget(result).then(completionsForObject);
168 } 179 }
169 180
170 /** 181 /**
182 * @param {!SDK.DebuggerModel.CallFrame} callFrame
183 * @param {function(!Array<!Components.JavaScriptAutocomplete.CompletionGroup> )} callback
184 */
185 function variableNamesInScopes(callFrame, callback) {
186 var result = [{items: ['this']}];
187
188 /**
189 * @param {string} name
190 * @param {?Array<!SDK.RemoteObjectProperty>} properties
191 */
192 function propertiesCollected(name, properties) {
193 var group = {title: name, items: []};
194 result.push(group);
195 for (var i = 0; properties && i < properties.length; ++i)
196 group.items.push(properties[i].name);
197 if (--pendingRequests === 0)
198 callback(result);
199 }
200
201 var scopeChain = callFrame.scopeChain();
202 var pendingRequests = scopeChain.length;
203 for (var i = 0; i < scopeChain.length; ++i) {
204 var scope = scopeChain[i];
205 var object = scope.object();
206 object.getAllProperties(false, propertiesCollected.bind(null, scope.typeNa me()));
207 }
208 }
209
210 /**
171 * @param {?SDK.RemoteObject} result 211 * @param {?SDK.RemoteObject} result
172 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails 212 * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails
173 */ 213 */
174 function receivedPropertyNamesFromEval(result, exceptionDetails) { 214 function receivedPropertyNamesFromEval(result, exceptionDetails) {
175 executionContext.target().runtimeAgent().releaseObjectGroup('completion'); 215 executionContext.target().runtimeAgent().releaseObjectGroup('completion');
176 if (result && !exceptionDetails) 216 if (result && !exceptionDetails)
177 receivedPropertyNames(/** @type {!Object} */ (result.value)); 217 receivedPropertyNames(/** @type {!Object} */ (result.value));
178 else 218 else
179 fufill([]); 219 fufill([]);
180 } 220 }
181 221
182 /** 222 /**
183 * @param {?Object} propertyNames 223 * @param {?Object} object
184 */ 224 */
185 function receivedPropertyNames(propertyNames) { 225 function receivedPropertyNames(object) {
186 executionContext.target().runtimeAgent().releaseObjectGroup('completion'); 226 executionContext.target().runtimeAgent().releaseObjectGroup('completion');
187 if (!propertyNames) { 227 if (!object) {
188 fufill([]); 228 fufill([]);
189 return; 229 return;
190 } 230 }
231 var propertyGroups = /** @type {!Array<!Components.JavaScriptAutocomplete.Co mpletionGroup>} */ (object);
191 var includeCommandLineAPI = (!dotNotation && !bracketNotation); 232 var includeCommandLineAPI = (!dotNotation && !bracketNotation);
192 if (includeCommandLineAPI) { 233 if (includeCommandLineAPI) {
193 const commandLineAPI = [ 234 const commandLineAPI = [
194 'dir', 235 'dir',
195 'dirxml', 236 'dirxml',
196 'keys', 237 'keys',
197 'values', 238 'values',
198 'profile', 239 'profile',
199 'profileEnd', 240 'profileEnd',
200 'monitorEvents', 241 'monitorEvents',
201 'unmonitorEvents', 242 'unmonitorEvents',
202 'inspect', 243 'inspect',
203 'copy', 244 'copy',
204 'clear', 245 'clear',
205 'getEventListeners', 246 'getEventListeners',
206 'debug', 247 'debug',
207 'undebug', 248 'undebug',
208 'monitor', 249 'monitor',
209 'unmonitor', 250 'unmonitor',
210 'table', 251 'table',
211 '$', 252 '$',
212 '$$', 253 '$$',
213 '$x' 254 '$x'
214 ]; 255 ];
215 for (var i = 0; i < commandLineAPI.length; ++i) 256 propertyGroups.push({items: commandLineAPI});
216 propertyNames[commandLineAPI[i]] = true;
217 } 257 }
218 fufill(Components.JavaScriptAutocomplete._completionsForQuery( 258 fufill(Components.JavaScriptAutocomplete._completionsForQuery(
219 dotNotation, bracketNotation, expressionString, query, Object.keys(prope rtyNames))); 259 dotNotation, bracketNotation, expressionString, query, propertyGroups));
220 } 260 }
221 }; 261 };
222 262
223 /** 263 /**
224 * @param {boolean} dotNotation 264 * @param {boolean} dotNotation
225 * @param {boolean} bracketNotation 265 * @param {boolean} bracketNotation
226 * @param {string} expressionString 266 * @param {string} expressionString
227 * @param {string} query 267 * @param {string} query
228 * @param {!Array.<string>} properties 268 * @param {!Array<!Components.JavaScriptAutocomplete.CompletionGroup>} propert yGroups
229 * @return {!UI.SuggestBox.Suggestions} 269 * @return {!UI.SuggestBox.Suggestions}
230 */ 270 */
231 Components.JavaScriptAutocomplete._completionsForQuery = function( 271 Components.JavaScriptAutocomplete._completionsForQuery = function(
232 dotNotation, bracketNotation, expressionString, query, properties) { 272 dotNotation, bracketNotation, expressionString, query, propertyGroups) {
233 if (bracketNotation) { 273 if (bracketNotation) {
234 if (query.length && query[0] === '\'') 274 if (query.length && query[0] === '\'')
235 var quoteUsed = '\''; 275 var quoteUsed = '\'';
236 else 276 else
237 var quoteUsed = '"'; 277 var quoteUsed = '"';
238 } 278 }
239 279
240 if (!expressionString) { 280 if (!expressionString) {
241 const keywords = [ 281 const keywords = [
242 'break', 'case', 'catch', 'continue', 'default', 'delete', 'do', 'else', 'finally', 282 'break', 'case', 'catch', 'continue', 'default', 'delete', 'do', 'else', 'finally',
243 'for', 'function', 'if', 'in', 'instanceof', 'new', 'return ', 'switch', 'this', 283 'for', 'function', 'if', 'in', 'instanceof', 'new', 'return ', 'switch', 'this',
244 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with' 284 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with'
245 ]; 285 ];
246 properties = properties.concat(keywords); 286 propertyGroups.push({title: Common.UIString('keywords'), items: keywords});
247 } 287 }
248 288
249 properties.sort(); 289 var result = [];
290 var lastGroupTitle;
291 for (var group of propertyGroups) {
292 group.items.sort();
293 var caseSensitivePrefix = [];
294 var caseInsensitivePrefix = [];
295 var caseSensitiveAnywhere = [];
296 var caseInsensitiveAnywhere = [];
250 297
251 var caseSensitivePrefix = []; 298 for (var property of group.items) {
252 var caseInsensitivePrefix = []; 299 // Assume that all non-ASCII characters are letters and thus can be used a s part of identifier.
253 var caseSensitiveAnywhere = []; 300 if (!bracketNotation && !/^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFF F]*$/.test(property))
254 var caseInsensitiveAnywhere = []; 301 continue;
255 for (var i = 0; i < properties.length; ++i) {
256 var property = properties[i];
257 302
258 // Assume that all non-ASCII characters are letters and thus can be used as part of identifier. 303 if (bracketNotation) {
259 if (!bracketNotation && !/^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFFF] *$/.test(property)) 304 if (!/^[0-9]+$/.test(property))
260 continue; 305 property = quoteUsed + property.escapeCharacters(quoteUsed + '\\') + q uoteUsed;
306 property += ']';
307 }
261 308
262 if (bracketNotation) { 309 if (property.length < query.length)
263 if (!/^[0-9]+$/.test(property)) 310 continue;
264 property = quoteUsed + property.escapeCharacters(quoteUsed + '\\') + quo teUsed; 311 if (query.length && property.toLowerCase().indexOf(query.toLowerCase()) == = -1)
265 property += ']'; 312 continue;
313 // Substitute actual newlines with newline characters. @see crbug.com/4984 21
314 var prop = property.split('\n').join('\\n');
315
316 if (property.startsWith(query))
317 caseSensitivePrefix.push({title: prop, priority: 4});
318 else if (property.toLowerCase().startsWith(query.toLowerCase()))
319 caseInsensitivePrefix.push({title: prop, priority: 3});
320 else if (property.indexOf(query) !== -1)
321 caseSensitiveAnywhere.push({title: prop, priority: 2});
322 else
323 caseInsensitiveAnywhere.push({title: prop, priority: 1});
266 } 324 }
267 325 var structuredGroup =
268 if (property.length < query.length) 326 caseSensitivePrefix.concat(caseInsensitivePrefix, caseSensitiveAnywhere, caseInsensitiveAnywhere);
269 continue; 327 if (structuredGroup.length && group.title !== lastGroupTitle) {
270 if (query.length && property.toLowerCase().indexOf(query.toLowerCase()) === -1) 328 structuredGroup[0].subtitle = group.title;
271 continue; 329 lastGroupTitle = group.title;
272 // Substitute actual newlines with newline characters. @see crbug.com/498421 330 }
273 var prop = property.split('\n').join('\\n'); 331 result = result.concat(structuredGroup);
274
275 if (property.startsWith(query))
276 caseSensitivePrefix.push(prop);
277 else if (property.toLowerCase().startsWith(query.toLowerCase()))
278 caseInsensitivePrefix.push(prop);
279 else if (property.indexOf(query) !== -1)
280 caseSensitiveAnywhere.push(prop);
281 else
282 caseInsensitiveAnywhere.push(prop);
283 } 332 }
284 return caseSensitivePrefix.concat(caseInsensitivePrefix) 333 return result;
285 .concat(caseSensitiveAnywhere)
286 .concat(caseInsensitiveAnywhere)
287 .map(completion => ({title: completion}));
288 }; 334 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698