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

Side by Side Diff: runtime/observatory/lib/src/debugger/debugger_location.dart

Issue 1393523002: Support tab completion of line:col in the debugger. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fix warnings and other issues Created 5 years, 2 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
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/debugger.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of debugger; 5 part of debugger;
6 6
7 class DebuggerLocation { 7 class DebuggerLocation {
8 DebuggerLocation.file(this.script, this.line, this.col); 8 DebuggerLocation.file(this.script, this.line, this.col);
9 DebuggerLocation.func(this.function); 9 DebuggerLocation.func(this.function);
10 DebuggerLocation.error(this.errorMessage); 10 DebuggerLocation.error(this.errorMessage);
(...skipping 24 matching lines...) Expand all
35 return _parseScriptLine(debugger, match); 35 return _parseScriptLine(debugger, match);
36 } 36 }
37 match = functionMatcher.firstMatch(locDesc); 37 match = functionMatcher.firstMatch(locDesc);
38 if (match != null) { 38 if (match != null) {
39 return _parseFunction(debugger, match); 39 return _parseFunction(debugger, match);
40 } 40 }
41 return new Future.value(new DebuggerLocation.error( 41 return new Future.value(new DebuggerLocation.error(
42 "Invalid source location '${locDesc}'")); 42 "Invalid source location '${locDesc}'"));
43 } 43 }
44 44
45 static Future<DebuggerLocation> _currentLocation(Debugger debugger) { 45 static Future<Frame> _currentFrame(Debugger debugger) async {
46 ServiceMap stack = debugger.stack; 46 ServiceMap stack = debugger.stack;
47 if (stack == null || stack['frames'].length == 0) { 47 if (stack == null || stack['frames'].length == 0) {
48 return new Future.value(new DebuggerLocation.error( 48 return null;
49 'A script must be provided when the stack is empty'));
50 } 49 }
51 var frame = stack['frames'][debugger.currentFrame]; 50 return stack['frames'][debugger.currentFrame];
51 }
52
53 static Future<DebuggerLocation> _currentLocation(Debugger debugger) async {
54 var frame = await _currentFrame(debugger);
55 if (frame == null) {
56 return new DebuggerLocation.error(
57 'A script must be provided when the stack is empty');
58 }
52 Script script = frame.location.script; 59 Script script = frame.location.script;
53 return script.load().then((_) { 60 await script.load();
54 var line = script.tokenToLine(frame.location.tokenPos); 61 var line = script.tokenToLine(frame.location.tokenPos);
55 var col = script.tokenToCol(frame.location.tokenPos); 62 var col = script.tokenToCol(frame.location.tokenPos);
56 return new Future.value(new DebuggerLocation.file(script, line, col)); 63 return new DebuggerLocation.file(script, line, col);
57 });
58 } 64 }
59 65
60 static Future<DebuggerLocation> _parseScriptLine(Debugger debugger, 66 static Future<DebuggerLocation> _parseScriptLine(Debugger debugger,
61 Match match) { 67 Match match) async {
62 var scriptName = match.group(1); 68 var scriptName = match.group(1);
63 if (scriptName != null) { 69 if (scriptName != null) {
64 scriptName = scriptName.substring(0, scriptName.length - 1); 70 scriptName = scriptName.substring(0, scriptName.length - 1);
65 } 71 }
66 var lineStr = match.group(2); 72 var lineStr = match.group(2);
67 assert(lineStr != null); 73 assert(lineStr != null);
68 var colStr = match.group(3); 74 var colStr = match.group(3);
69 if (colStr != null) { 75 if (colStr != null) {
70 colStr = colStr.substring(1); 76 colStr = colStr.substring(1);
71 } 77 }
72 var line = int.parse(lineStr, onError:(_) => -1); 78 var line = int.parse(lineStr, onError:(_) => -1);
73 var col = (colStr != null 79 var col = (colStr != null
74 ? int.parse(colStr, onError:(_) => -1) 80 ? int.parse(colStr, onError:(_) => -1)
75 : null); 81 : null);
76 if (line == -1) { 82 if (line == -1) {
77 return new Future.value(new DebuggerLocation.error( 83 return new Future.value(new DebuggerLocation.error(
78 "Line '${lineStr}' must be an integer")); 84 "Line '${lineStr}' must be an integer"));
79 } 85 }
80 if (col == -1) { 86 if (col == -1) {
81 return new Future.value(new DebuggerLocation.error( 87 return new Future.value(new DebuggerLocation.error(
82 "Column '${colStr}' must be an integer")); 88 "Column '${colStr}' must be an integer"));
83 } 89 }
84 90
85 if (scriptName != null) { 91 if (scriptName != null) {
86 // Resolve the script. 92 // Resolve the script.
87 return _lookupScript(debugger.isolate, scriptName).then((scripts) { 93 var scripts = await _lookupScript(debugger.isolate, scriptName);
88 if (scripts.length == 0) { 94 if (scripts.length == 0) {
89 return new DebuggerLocation.error("Script '${scriptName}' not found"); 95 return new DebuggerLocation.error("Script '${scriptName}' not found");
90 } else if (scripts.length == 1) { 96 } else if (scripts.length == 1) {
91 return new DebuggerLocation.file(scripts[0], line, col); 97 return new DebuggerLocation.file(scripts[0], line, col);
92 } else { 98 } else {
93 // TODO(turnidge): Allow the user to disambiguate. 99 // TODO(turnidge): Allow the user to disambiguate.
94 return new DebuggerLocation.error("Script '${scriptName}' is ambigous" ); 100 return new DebuggerLocation.error("Script '${scriptName}' is ambigous");
95 } 101 }
96 });
97 } else { 102 } else {
98 // No script provided. Default to top of stack for now. 103 // No script provided. Default to top of stack for now.
99 ServiceMap stack = debugger.stack; 104 var frame = await _currentFrame(debugger);
100 if (stack == null || stack['frames'].length == 0) { 105 if (frame == null) {
101 return new Future.value(new DebuggerLocation.error( 106 return new Future.value(new DebuggerLocation.error(
102 'A script must be provided when the stack is empty')); 107 'A script must be provided when the stack is empty'));
103 } 108 }
104 var frame = stack['frames'][debugger.currentFrame];
105 Script script = frame.location.script; 109 Script script = frame.location.script;
106 return script.load().then((_) { 110 await script.load();
107 return new Future.value(new DebuggerLocation.file(script, line, col)); 111 return new DebuggerLocation.file(script, line, col);
108 });
109 } 112 }
110 } 113 }
111 114
112 static Future<List<Script>> _lookupScript(Isolate isolate, 115 static Future<List<Script>> _lookupScript(Isolate isolate,
113 String name, 116 String name,
114 {bool allowPrefix: false}) { 117 {bool allowPrefix: false}) {
115 var pending = []; 118 var pending = [];
116 for (var lib in isolate.libraries) { 119 for (var lib in isolate.libraries) {
117 if (!lib.loaded) { 120 if (!lib.loaded) {
118 pending.add(lib.load()); 121 pending.add(lib.load());
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 matches.add(function); 156 matches.add(function);
154 } 157 }
155 } 158 }
156 } 159 }
157 } 160 }
158 return matches; 161 return matches;
159 } 162 }
160 163
161 static Future<List<Class>> _lookupClass(Isolate isolate, 164 static Future<List<Class>> _lookupClass(Isolate isolate,
162 String name, 165 String name,
163 { bool allowPrefix: false }) { 166 { bool allowPrefix: false }) async {
167 if (isolate == null) {
168 return [];
169 }
164 var pending = []; 170 var pending = [];
165 for (var lib in isolate.libraries) { 171 for (var lib in isolate.libraries) {
166 assert(lib.loaded); 172 assert(lib.loaded);
167 for (var cls in lib.classes) { 173 for (var cls in lib.classes) {
168 if (!cls.loaded) { 174 if (!cls.loaded) {
169 pending.add(cls.load()); 175 pending.add(cls.load());
170 } 176 }
171 } 177 }
172 } 178 }
173 return Future.wait(pending).then((_) { 179 await Future.wait(pending);
174 var matches = []; 180 var matches = [];
175 for (var lib in isolate.libraries) { 181 for (var lib in isolate.libraries) {
176 for (var cls in lib.classes) { 182 for (var cls in lib.classes) {
177 if (allowPrefix) { 183 if (allowPrefix) {
178 if (cls.name.startsWith(name)) { 184 if (cls.name.startsWith(name)) {
179 matches.add(cls); 185 matches.add(cls);
180 } 186 }
181 } else { 187 } else {
182 if (name == cls.name) { 188 if (name == cls.name) {
183 matches.add(cls); 189 matches.add(cls);
184 }
185 } 190 }
186 } 191 }
187 } 192 }
188 return matches; 193 }
189 }); 194 return matches;
190 } 195 }
191 196
192 static ServiceFunction _getConstructor(Class cls, String name) { 197 static ServiceFunction _getConstructor(Class cls, String name) {
193 for (var function in cls.functions) { 198 for (var function in cls.functions) {
194 assert(cls.loaded); 199 assert(cls.loaded);
195 if (name == function.name) { 200 if (name == function.name) {
196 return function; 201 return function;
197 } 202 }
198 } 203 }
199 return null; 204 return null;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 } else { 253 } else {
249 // TODO(turnidge): Allow the user to disambiguate. 254 // TODO(turnidge): Allow the user to disambiguate.
250 return new DebuggerLocation.error( 255 return new DebuggerLocation.error(
251 "Function '${match.group(0)}' is ambigous"); 256 "Function '${match.group(0)}' is ambigous");
252 } 257 }
253 return new DebuggerLocation.error('foo'); 258 return new DebuggerLocation.error('foo');
254 }); 259 });
255 } 260 }
256 261
257 static RegExp partialSourceLocMatcher = 262 static RegExp partialSourceLocMatcher =
258 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d+)?'); 263 new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d*)?');
259 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?'); 264 static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?');
260 265
261 /// Completes a partial source location description. 266 /// Completes a partial source location description.
262 static Future<List<String>> complete(Debugger debugger, String locDesc) { 267 static Future<List<String>> complete(Debugger debugger, String locDesc) {
263 List<Future<List<String>>> pending = []; 268 List<Future<List<String>>> pending = [];
264 var match = partialFunctionMatcher.firstMatch(locDesc); 269 var match = partialFunctionMatcher.firstMatch(locDesc);
265 if (match != null) { 270 if (match != null) {
266 pending.add(_completeFunction(debugger, match)); 271 pending.add(_completeFunction(debugger, match));
267 } 272 }
268 273
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 } 324 }
320 } 325 }
321 } 326 }
322 } 327 }
323 completions.sort(); 328 completions.sort();
324 return completions; 329 return completions;
325 }); 330 });
326 } 331 }
327 } 332 }
328 333
329 static Future<List<String>> _completeFile(Debugger debugger, Match match) { 334 static bool _startsWithDigit(String s) {
330 var scriptName = match.group(1); 335 return '0'.compareTo(s[0]) <= 0 && '9'.compareTo(s[0]) >= 0;
331 var lineStr = match.group(2); 336 }
332 var colStr = match.group(3); 337
333 if (lineStr != null || colStr != null) { 338 static Future<List<String>> _completeFile(
334 // TODO(turnidge): Complete valid line and column numbers. 339 Debugger debugger, Match match) async {
335 return new Future.value([]); 340 var scriptName;
341 var scriptNameComplete = false;
342 var lineStr;
343 var lineStrComplete = false;
344 var colStr;
345 if (_startsWithDigit(match.group(1))) {
346 // CASE 1: We have matched a prefix of (lineStr:)(colStr)
347 var frame = await _currentFrame(debugger);
348 if (frame == null) {
349 return [];
350 }
351 scriptName = frame.location.script.name;
352 scriptNameComplete = true;
353 lineStr = match.group(1);
354 lineStr = (lineStr == null ? '' : lineStr);
355 if (lineStr.endsWith(':')) {
356 lineStr = lineStr.substring(0, lineStr.length - 1);
357 lineStrComplete = true;
358 }
359 colStr = match.group(2);
360 colStr = (colStr == null ? '' : colStr);
361 } else {
362 // CASE 2: We have matched a prefix of (scriptName:)(lineStr)(:colStr)
363 scriptName = match.group(1);
364 scriptName = (scriptName == null ? '' : scriptName);
365 if (scriptName.endsWith(':')) {
366 scriptName = scriptName.substring(0, scriptName.length - 1);
367 scriptNameComplete = true;
368 }
369 lineStr = match.group(2);
370 lineStr = (lineStr == null ? '' : lineStr);
371 colStr = match.group(3);
372 colStr = (colStr == null ? '' : colStr);
373 if (colStr.startsWith(':')) {
374 lineStrComplete = true;
375 colStr = colStr.substring(1);
376 }
336 } 377 }
337 scriptName = (scriptName == null ? '' : scriptName);
338 378
339 return _lookupScript(debugger.isolate, scriptName, allowPrefix:true) 379 if (!scriptNameComplete) {
340 .then((scripts) { 380 // The script name is incomplete. Complete it.
381 var scripts =
382 await _lookupScript(debugger.isolate, scriptName, allowPrefix:true);
383 List completions = [];
384 for (var script in scripts) {
385 completions.add(script.name + ':');
386 }
387 completions.sort();
388 return completions;
389
390 } else {
391 // The script name is complete. Look it up.
392 var scripts =
393 await _lookupScript(debugger.isolate, scriptName, allowPrefix:false);
394 if (scripts.isEmpty) {
395 return [];
396 }
397 var script = scripts[0];
398 await script.load();
399 if (!lineStrComplete) {
400 // Complete the line.
401 var sharedPrefix = '${script.name}:';
341 List completions = []; 402 List completions = [];
342 for (var script in scripts) { 403 for (var line in script.lines) {
343 completions.add(script.name + ':'); 404 if (line.possibleBpt) {
405 var currentLineStr = line.line.toString();
406 if (currentLineStr.startsWith(lineStr)) {
407 completions.add('${sharedPrefix}${currentLineStr} ');
408 completions.add('${sharedPrefix}${currentLineStr}:');
409 }
410 }
344 } 411 }
345 completions.sort();
346 return completions; 412 return completions;
347 }); 413
414 } else {
415 // Complete the column.
416 int lineNum = int.parse(lineStr);
417 var scriptLine = script.getLine(lineNum);
418 if (!scriptLine.possibleBpt) {
419 return [];
420 }
421 var sharedPrefix = '${script.name}:${lineStr}:';
422 List completions = [];
423 int maxCol = scriptLine.text.runes.length;
rmacnak 2015/10/07 18:03:09 DBC: It would be good to have a test with characte
424 for (int i = 1; i <= maxCol; i++) {
425 var currentColStr = i.toString();
426 if (currentColStr.startsWith(colStr)) {
427 completions.add('${sharedPrefix}${currentColStr} ');
428 }
429 }
430 return completions;
431 }
432 }
348 } 433 }
349 434
350 String toString() { 435 String toString() {
351 if (valid) { 436 if (valid) {
352 if (function != null) { 437 if (function != null) {
353 return '${function.qualifiedName}'; 438 return '${function.qualifiedName}';
354 } else if (col != null) { 439 } else if (col != null) {
355 return '${script.name}:${line}:${col}'; 440 return '${script.name}:${line}:${col}';
356 } else { 441 } else {
357 return '${script.name}:${line}'; 442 return '${script.name}:${line}';
358 } 443 }
359 } 444 }
360 return 'invalid source location (${errorMessage})'; 445 return 'invalid source location (${errorMessage})';
361 } 446 }
362 447
363 Script script; 448 Script script;
364 int line; 449 int line;
365 int col; 450 int col;
366 ServiceFunction function; 451 ServiceFunction function;
367 String errorMessage; 452 String errorMessage;
368 bool get valid => (errorMessage == null); 453 bool get valid => (errorMessage == null);
369 } 454 }
OLDNEW
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/debugger.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698