| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library debugger_page_element; | 5 library debugger_page_element; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:html'; | 8 import 'dart:html'; |
| 9 import 'observatory_element.dart'; | 9 import 'observatory_element.dart'; |
| 10 import 'package:observatory/cli.dart'; | 10 import 'package:observatory/cli.dart'; |
| 11 import 'package:observatory/debugger.dart'; |
| 11 import 'package:observatory/service.dart'; | 12 import 'package:observatory/service.dart'; |
| 12 import 'package:polymer/polymer.dart'; | 13 import 'package:polymer/polymer.dart'; |
| 13 | 14 |
| 14 // TODO(turnidge): Move Debugger, DebuggerCommand to their own lib. | 15 // TODO(turnidge): Move Debugger, DebuggerCommand to debugger library. |
| 15 abstract class DebuggerCommand extends Command { | 16 abstract class DebuggerCommand extends Command { |
| 16 Debugger debugger; | 17 ObservatoryDebugger debugger; |
| 17 | 18 |
| 18 DebuggerCommand(this.debugger, name, children) | 19 DebuggerCommand(this.debugger, name, children) |
| 19 : super(name, children); | 20 : super(name, children); |
| 20 } | 21 } |
| 21 | 22 |
| 22 class HelpCommand extends DebuggerCommand { | 23 class HelpCommand extends DebuggerCommand { |
| 23 HelpCommand(Debugger debugger) : super(debugger, 'help', []); | 24 HelpCommand(Debugger debugger) : super(debugger, 'help', []); |
| 24 | 25 |
| 25 Future run(List<String> args) { | 26 Future run(List<String> args) { |
| 26 var con = debugger.console; | 27 var con = debugger.console; |
| 27 con.printLine('List of commands:'); | 28 con.printLine('List of commands:'); |
| 28 con.newline(); | 29 con.newline(); |
| 29 | 30 |
| 30 // TODO(turnidge): Build a real help system. | 31 // TODO(turnidge): Build a real help system. |
| 31 List completions = debugger.cmd.completeCommand(''); | 32 return debugger.cmd.completeCommand('').then((completions) { |
| 32 completions = completions.map((s )=> s.trimRight()).toList(); | 33 completions = completions.map((s) => s.trimRight()).toList(); |
| 33 completions.sort(); | 34 completions.sort(); |
| 34 con.printLine(completions.toString()); | 35 con.printLine(completions.toString()); |
| 35 con.newline(); | 36 con.newline(); |
| 36 con.printLine("Command prefixes are accepted (e.g. 'h' for 'help')"); | 37 con.printLine("Command prefixes are accepted (e.g. 'h' for 'help')"); |
| 37 con.printLine("Hit [TAB] to complete a command (try 'i[TAB][TAB]')"); | 38 con.printLine("Hit [TAB] to complete a command (try 'i[TAB][TAB]')"); |
| 38 con.printLine("Hit [ENTER] to repeat the last command"); | 39 con.printLine("Hit [ENTER] to repeat the last command"); |
| 39 | 40 }); |
| 40 return new Future.value(null); | |
| 41 } | 41 } |
| 42 } | 42 } |
| 43 | 43 |
| 44 class PauseCommand extends DebuggerCommand { | 44 class PauseCommand extends DebuggerCommand { |
| 45 PauseCommand(Debugger debugger) : super(debugger, 'pause', []); | 45 PauseCommand(Debugger debugger) : super(debugger, 'pause', []); |
| 46 | 46 |
| 47 Future run(List<String> args) { | 47 Future run(List<String> args) { |
| 48 if (!debugger.isolatePaused()) { | 48 if (!debugger.isolatePaused()) { |
| 49 return debugger.isolate.pause(); | 49 return debugger.isolate.pause(); |
| 50 } else { | 50 } else { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 Future run(List<String> args) { | 119 Future run(List<String> args) { |
| 120 if (debugger.isolatePaused()) { | 120 if (debugger.isolatePaused()) { |
| 121 return debugger.isolate.stepOut(); | 121 return debugger.isolate.stepOut(); |
| 122 } else { | 122 } else { |
| 123 debugger.console.printLine('The program is already running'); | 123 debugger.console.printLine('The program is already running'); |
| 124 return new Future.value(null); | 124 return new Future.value(null); |
| 125 } | 125 } |
| 126 } | 126 } |
| 127 } | 127 } |
| 128 | 128 |
| 129 class BreakCommand extends DebuggerCommand { |
| 130 BreakCommand(Debugger debugger) : super(debugger, 'break', []); |
| 131 |
| 132 Future run(List<String> args) { |
| 133 if (args.length > 1) { |
| 134 debugger.console.printLine('not implemented'); |
| 135 return new Future.value(null); |
| 136 } |
| 137 var arg = (args.length == 0 ? '' : args[0]); |
| 138 return SourceLocation.parse(debugger, arg).then((loc) { |
| 139 if (loc.valid) { |
| 140 if (loc.function != null) { |
| 141 debugger.console.printLine( |
| 142 'Ignoring breakpoint at $loc: ' |
| 143 'Function entry breakpoints not yet implemented'); |
| 144 return null; |
| 145 } |
| 146 if (loc.col != null) { |
| 147 // TODO(turnidge): Add tokenPos breakpoint support. |
| 148 debugger.console.printLine( |
| 149 'Ignoring column: ' |
| 150 'adding breakpoint at a specific column not yet implemented'); |
| 151 } |
| 152 return debugger.isolate.addBreakpoint(loc.script, loc.line).then((result
) { |
| 153 if (result is DartError) { |
| 154 debugger.console.printLine('Unable to set breakpoint at ${loc}'); |
| 155 } else { |
| 156 // TODO(turnidge): Adding a duplicate breakpoint is |
| 157 // currently ignored. May want to change the protocol to |
| 158 // inform us when this happens. |
| 159 |
| 160 // The BreakpointResolved event prints resolved |
| 161 // breakpoints already. Just print the unresolved ones here. |
| 162 ServiceMap bpt = result; |
| 163 if (!bpt['resolved']) { |
| 164 var script = bpt['location']['script']; |
| 165 var bpId = bpt['breakpointNumber']; |
| 166 var tokenPos = bpt['location']['tokenPos']; |
| 167 return script.load().then((_) { |
| 168 var line = script.tokenToLine(tokenPos); |
| 169 var col = script.tokenToCol(tokenPos); |
| 170 debugger.console.printLine( |
| 171 'Future breakpoint ${bpId} added at ' |
| 172 '${script.name}:${line}:${col}'); |
| 173 }); |
| 174 } |
| 175 } |
| 176 }); |
| 177 } else { |
| 178 debugger.console.printLine(loc.errorMessage); |
| 179 } |
| 180 }); |
| 181 } |
| 182 |
| 183 Future<List<String>> complete(List<String> args) { |
| 184 if (args.length != 1) { |
| 185 return new Future.value([]); |
| 186 } |
| 187 // TODO - fix SourceLocation complete |
| 188 return new Future.value(SourceLocation.complete(debugger, args[0])); |
| 189 } |
| 190 } |
| 191 |
| 192 class ClearCommand extends DebuggerCommand { |
| 193 ClearCommand(Debugger debugger) : super(debugger, 'clear', []); |
| 194 |
| 195 Future run(List<String> args) { |
| 196 if (args.length > 1) { |
| 197 debugger.console.printLine('not implemented'); |
| 198 return new Future.value(null); |
| 199 } |
| 200 var arg = (args.length == 0 ? '' : args[0]); |
| 201 return SourceLocation.parse(debugger, arg).then((loc) { |
| 202 if (loc.valid) { |
| 203 if (loc.function != null) { |
| 204 debugger.console.printLine( |
| 205 'Ignoring breakpoint at $loc: ' |
| 206 'Function entry breakpoints not yet implemented'); |
| 207 return null; |
| 208 } |
| 209 if (loc.col != null) { |
| 210 // TODO(turnidge): Add tokenPos clear support. |
| 211 debugger.console.printLine( |
| 212 'Ignoring column: ' |
| 213 'clearing breakpoint at a specific column not yet implemented'); |
| 214 } |
| 215 |
| 216 for (var bpt in debugger.isolate.breakpoints) { |
| 217 var script = bpt['location']['script']; |
| 218 if (script.id == loc.script.id) { |
| 219 assert(script.loaded); |
| 220 var line = script.tokenToLine(bpt['location']['tokenPos']); |
| 221 if (line == loc.line) { |
| 222 return debugger.isolate.removeBreakpoint(bpt).then((result) { |
| 223 if (result is DartError) { |
| 224 debugger.console.printLine( |
| 225 'Unable to clear breakpoint at ${loc}: ${result.message}')
; |
| 226 return; |
| 227 } else { |
| 228 // TODO(turnidge): Add a BreakpointRemoved event to |
| 229 // the service instead of printing here. |
| 230 var bpId = bpt['breakpointNumber']; |
| 231 debugger.console.printLine( |
| 232 'Breakpoint ${bpId} removed at ${loc}'); |
| 233 return; |
| 234 } |
| 235 }); |
| 236 } |
| 237 } |
| 238 } |
| 239 debugger.console.printLine('No breakpoint found at ${loc}'); |
| 240 } else { |
| 241 debugger.console.printLine(loc.errorMessage); |
| 242 } |
| 243 }); |
| 244 } |
| 245 |
| 246 Future<List<String>> complete(List<String> args) { |
| 247 if (args.length != 1) { |
| 248 return new Future.value([]); |
| 249 } |
| 250 return new Future.value(SourceLocation.complete(debugger, args[0])); |
| 251 } |
| 252 } |
| 253 |
| 129 // TODO(turnidge): Add argument completion. | 254 // TODO(turnidge): Add argument completion. |
| 130 class DeleteCommand extends DebuggerCommand { | 255 class DeleteCommand extends DebuggerCommand { |
| 131 DeleteCommand(Debugger debugger) : super(debugger, 'delete', []); | 256 DeleteCommand(Debugger debugger) : super(debugger, 'delete', []); |
| 132 | 257 |
| 133 Future run(List<String> args) { | 258 Future run(List<String> args) { |
| 134 if (args.length < 1) { | 259 if (args.length < 1) { |
| 135 debugger.console.printLine('delete expects one or more arguments'); | 260 debugger.console.printLine('delete expects one or more arguments'); |
| 136 return new Future.value(null); | 261 return new Future.value(null); |
| 137 } | 262 } |
| 138 List toDelete = []; | 263 List toDelete = []; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 return debugger.isolate.reloadBreakpoints().then((_) { | 295 return debugger.isolate.reloadBreakpoints().then((_) { |
| 171 if (debugger.isolate.breakpoints.isEmpty) { | 296 if (debugger.isolate.breakpoints.isEmpty) { |
| 172 debugger.console.printLine('No breakpoints'); | 297 debugger.console.printLine('No breakpoints'); |
| 173 } | 298 } |
| 174 for (var bpt in debugger.isolate.breakpoints) { | 299 for (var bpt in debugger.isolate.breakpoints) { |
| 175 var bpId = bpt['breakpointNumber']; | 300 var bpId = bpt['breakpointNumber']; |
| 176 var script = bpt['location']['script']; | 301 var script = bpt['location']['script']; |
| 177 var tokenPos = bpt['location']['tokenPos']; | 302 var tokenPos = bpt['location']['tokenPos']; |
| 178 var line = script.tokenToLine(tokenPos); | 303 var line = script.tokenToLine(tokenPos); |
| 179 var col = script.tokenToCol(tokenPos); | 304 var col = script.tokenToCol(tokenPos); |
| 305 var extras = new StringBuffer(); |
| 306 if (!bpt['resolved']) { |
| 307 extras.write(' unresolved'); |
| 308 } |
| 309 if (!bpt['enabled']) { |
| 310 extras.write(' disabled'); |
| 311 } |
| 180 debugger.console.printLine( | 312 debugger.console.printLine( |
| 181 'Breakpoint ${bpId} at ${script.name}:${line}:${col}'); | 313 'Breakpoint ${bpId} at ${script.name}:${line}:${col}${extras}'); |
| 182 } | 314 } |
| 183 }); | 315 }); |
| 184 } | 316 } |
| 185 } | 317 } |
| 186 | 318 |
| 187 class InfoIsolatesCommand extends DebuggerCommand { | 319 class InfoIsolatesCommand extends DebuggerCommand { |
| 188 InfoIsolatesCommand(Debugger debugger) : super(debugger, 'isolates', []); | 320 InfoIsolatesCommand(Debugger debugger) : super(debugger, 'isolates', []); |
| 189 | 321 |
| 190 Future run(List<String> args) { | 322 Future run(List<String> args) { |
| 191 for (var isolate in debugger.isolate.vm.isolates) { | 323 for (var isolate in debugger.isolate.vm.isolates) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 227 RefreshCommand(Debugger debugger) : super(debugger, 'refresh', [ | 359 RefreshCommand(Debugger debugger) : super(debugger, 'refresh', [ |
| 228 new RefreshCoverageCommand(debugger), | 360 new RefreshCoverageCommand(debugger), |
| 229 ]); | 361 ]); |
| 230 | 362 |
| 231 Future run(List<String> args) { | 363 Future run(List<String> args) { |
| 232 return debugger.refreshStack(); | 364 return debugger.refreshStack(); |
| 233 } | 365 } |
| 234 } | 366 } |
| 235 | 367 |
| 236 // Tracks the state for an isolate debugging session. | 368 // Tracks the state for an isolate debugging session. |
| 237 class Debugger { | 369 class ObservatoryDebugger extends Debugger { |
| 238 RootCommand cmd; | 370 RootCommand cmd; |
| 239 DebuggerConsoleElement console; | 371 DebuggerConsoleElement console; |
| 240 DebuggerStackElement stackElement; | 372 DebuggerStackElement stackElement; |
| 241 ServiceMap stack; | 373 ServiceMap stack; |
| 374 int currentFrame = 0; |
| 242 | 375 |
| 243 Debugger() { | 376 ObservatoryDebugger() { |
| 244 cmd = new RootCommand([ | 377 cmd = new RootCommand([ |
| 245 new HelpCommand(this), | 378 new HelpCommand(this), |
| 246 new PauseCommand(this), | 379 new PauseCommand(this), |
| 247 new ContinueCommand(this), | 380 new ContinueCommand(this), |
| 248 new NextCommand(this), | 381 new NextCommand(this), |
| 249 new StepCommand(this), | 382 new StepCommand(this), |
| 250 new FinishCommand(this), | 383 new FinishCommand(this), |
| 384 new BreakCommand(this), |
| 385 new ClearCommand(this), |
| 251 new DeleteCommand(this), | 386 new DeleteCommand(this), |
| 252 new InfoCommand(this), | 387 new InfoCommand(this), |
| 253 new RefreshCommand(this), | 388 new RefreshCommand(this), |
| 254 ]); | 389 ]); |
| 255 } | 390 } |
| 256 | 391 |
| 257 void set isolate(Isolate iso) { | 392 void set isolate(Isolate iso) { |
| 258 _isolate = iso; | 393 _isolate = iso; |
| 259 if (_isolate != null) { | 394 if (_isolate != null) { |
| 260 _isolate.reload().then((_) { | 395 _isolate.reload().then((_) { |
| 261 _isolate.vm.events.stream.listen(_onEvent); | 396 // TODO(turnidge): Currently the debugger relies on all libs |
| 262 _refreshStack(isolate.pauseEvent).then((_) { | 397 // being loaded. Fix this. |
| 263 reportStatus(); | 398 var pending = []; |
| 399 for (var lib in _isolate.libraries) { |
| 400 if (!lib.loaded) { |
| 401 pending.add(lib.load()); |
| 402 } |
| 403 } |
| 404 Future.wait(pending).then((_) { |
| 405 _isolate.vm.events.stream.listen(_onEvent); |
| 406 _refreshStack(isolate.pauseEvent).then((_) { |
| 407 reportStatus(); |
| 408 }); |
| 264 }); | 409 }); |
| 265 }); | 410 }); |
| 266 } | 411 } |
| 267 } | 412 } |
| 268 Isolate get isolate => _isolate; | 413 Isolate get isolate => _isolate; |
| 269 Isolate _isolate; | 414 Isolate _isolate; |
| 270 | 415 |
| 271 void init() { | 416 void init() { |
| 272 console.newline(); | 417 console.newline(); |
| 273 console.printBold("Type 'h' for help"); | 418 console.printBold("Type 'h' for help"); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 293 stackElement.isSampled = true; | 438 stackElement.isSampled = true; |
| 294 } | 439 } |
| 295 }); | 440 }); |
| 296 } | 441 } |
| 297 | 442 |
| 298 Future<ServiceMap> _refreshStack(ServiceEvent pauseEvent) { | 443 Future<ServiceMap> _refreshStack(ServiceEvent pauseEvent) { |
| 299 return isolate.getStack().then((result) { | 444 return isolate.getStack().then((result) { |
| 300 stack = result; | 445 stack = result; |
| 301 // TODO(turnidge): Replace only the changed part of the stack to | 446 // TODO(turnidge): Replace only the changed part of the stack to |
| 302 // reduce flicker. | 447 // reduce flicker. |
| 303 // stackElement.stack = stack; | |
| 304 stackElement.updateStack(stack, pauseEvent); | 448 stackElement.updateStack(stack, pauseEvent); |
| 305 }); | 449 }); |
| 306 } | 450 } |
| 307 | 451 |
| 308 void reportStatus() { | 452 void reportStatus() { |
| 309 if (_isolate.idle) { | 453 if (_isolate.idle) { |
| 310 console.printLine('Isolate is idle'); | 454 console.printLine('Isolate is idle'); |
| 311 } else if (_isolate.running) { | 455 } else if (_isolate.running) { |
| 312 console.printLine("Isolate is running (type 'pause' to interrupt)"); | 456 console.printLine("Isolate is running (type 'pause' to interrupt)"); |
| 313 } else if (_isolate.pauseEvent != null) { | 457 } else if (_isolate.pauseEvent != null) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 360 case 'ExceptionThrown': | 504 case 'ExceptionThrown': |
| 361 _refreshStack(event).then((_) { | 505 _refreshStack(event).then((_) { |
| 362 _reportPause(event); | 506 _reportPause(event); |
| 363 }); | 507 }); |
| 364 break; | 508 break; |
| 365 | 509 |
| 366 case 'IsolateResumed': | 510 case 'IsolateResumed': |
| 367 console.printLine('Continuing...'); | 511 console.printLine('Continuing...'); |
| 368 break; | 512 break; |
| 369 | 513 |
| 514 case 'BreakpointResolved': |
| 515 var bpId = event.breakpoint['breakpointNumber']; |
| 516 var script = event.breakpoint['location']['script']; |
| 517 var tokenPos = event.breakpoint['location']['tokenPos']; |
| 518 var line = script.tokenToLine(tokenPos); |
| 519 var col = script.tokenToCol(tokenPos); |
| 520 console.printLine( |
| 521 'Breakpoint ${bpId} added at ${script.name}:${line}:${col}'); |
| 522 break; |
| 523 |
| 370 case '_Graph': | 524 case '_Graph': |
| 371 case 'BreakpointResolved': | |
| 372 case 'IsolateCreated': | 525 case 'IsolateCreated': |
| 373 case 'GC': | 526 case 'GC': |
| 374 // Ignore these events for now. | 527 // Ignore these events for now. |
| 375 break; | 528 break; |
| 376 | 529 |
| 377 default: | 530 default: |
| 378 console.printLine('Unrecognized event: $event'); | 531 console.printLine('Unrecognized event: $event'); |
| 379 break; | 532 break; |
| 380 } | 533 } |
| 381 } | 534 } |
| 382 | 535 |
| 383 String complete(String line) { | 536 static String _commonPrefix(String a, String b) { |
| 384 List<String> completions = cmd.completeCommand(line); | 537 int pos = 0; |
| 385 if (completions.length == 0) { | 538 while (pos < a.length && pos < b.length) { |
| 386 // No completions. Leave the line alone. | 539 if (a.codeUnitAt(pos) != b.codeUnitAt(pos)) { |
| 387 return line; | 540 break; |
| 388 } else if (completions.length == 1) { | 541 } |
| 389 // Unambiguous completion. | 542 pos++; |
| 390 return completions[0]; | 543 } |
| 391 } else { | 544 return a.substring(0, pos); |
| 392 // Ambigous completion. | 545 } |
| 393 completions = completions.map((s )=> s.trimRight()).toList(); | |
| 394 completions.sort(); | |
| 395 console.printBold(completions.toString()); | |
| 396 | 546 |
| 397 // TODO(turnidge): Complete to common prefix of all completions. | 547 static String _foldCompletions(List<String> values) { |
| 398 return line; | 548 if (values.length == 0) { |
| 549 return ''; |
| 399 } | 550 } |
| 551 var prefix = values[0]; |
| 552 for (int i = 1; i < values.length; i++) { |
| 553 prefix = _commonPrefix(prefix, values[i]); |
| 554 } |
| 555 return prefix; |
| 556 } |
| 557 |
| 558 Future<String> complete(String line) { |
| 559 return cmd.completeCommand(line).then((completions) { |
| 560 if (completions.length == 0) { |
| 561 // No completions. Leave the line alone. |
| 562 return line; |
| 563 } else if (completions.length == 1) { |
| 564 // Unambiguous completion. |
| 565 return completions[0]; |
| 566 } else { |
| 567 // Ambigous completion. |
| 568 completions = completions.map((s )=> s.trimRight()).toList(); |
| 569 console.printBold(completions.toString()); |
| 570 return _foldCompletions(completions); |
| 571 } |
| 572 }); |
| 400 } | 573 } |
| 401 | 574 |
| 402 // TODO(turnidge): Implement real command line history. | 575 // TODO(turnidge): Implement real command line history. |
| 403 String lastCommand; | 576 String lastCommand; |
| 404 bool busy = false; | |
| 405 | 577 |
| 406 Future run(String command) { | 578 Future run(String command) { |
| 407 assert(!busy); | 579 if (command == '' && lastCommand != null) { |
| 408 busy = true; | |
| 409 if (command == '') { | |
| 410 command = lastCommand; | 580 command = lastCommand; |
| 411 } | 581 } |
| 412 lastCommand = command; | |
| 413 console.printBold('\$ $command'); | 582 console.printBold('\$ $command'); |
| 414 return cmd.runCommand(command).then((_) { | 583 return cmd.runCommand(command).then((_) { |
| 415 busy = false; | 584 lastCommand = command; |
| 416 }).catchError((e) { | 585 }).catchError((e) { |
| 417 console.printLine('ERROR $e'); | 586 console.printLine('ERROR $e'); |
| 418 }); | 587 }); |
| 419 } | 588 } |
| 420 } | 589 } |
| 421 | 590 |
| 422 @CustomTag('debugger-page') | 591 @CustomTag('debugger-page') |
| 423 class DebuggerPageElement extends ObservatoryElement { | 592 class DebuggerPageElement extends ObservatoryElement { |
| 424 @published Isolate isolate; | 593 @published Isolate isolate; |
| 425 | 594 |
| 426 isolateChanged(oldValue) { | 595 isolateChanged(oldValue) { |
| 427 if (isolate != null) { | 596 if (isolate != null) { |
| 428 debugger.isolate = isolate; | 597 debugger.isolate = isolate; |
| 429 } | 598 } |
| 430 } | 599 } |
| 431 Debugger debugger = new Debugger(); | 600 ObservatoryDebugger debugger = new ObservatoryDebugger(); |
| 432 | 601 |
| 433 DebuggerPageElement.created() : super.created(); | 602 DebuggerPageElement.created() : super.created(); |
| 434 | 603 |
| 435 @override | 604 @override |
| 436 void attached() { | 605 void attached() { |
| 437 super.attached(); | 606 super.attached(); |
| 438 | 607 |
| 439 var navbarDiv = $['navbarDiv']; | 608 var navbarDiv = $['navbarDiv']; |
| 440 var stackDiv = $['stackDiv']; | 609 var stackDiv = $['stackDiv']; |
| 441 var splitterDiv = $['splitterDiv']; | 610 var splitterDiv = $['splitterDiv']; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 461 debugger.init(); | 630 debugger.init(); |
| 462 } | 631 } |
| 463 | 632 |
| 464 } | 633 } |
| 465 | 634 |
| 466 @CustomTag('debugger-stack') | 635 @CustomTag('debugger-stack') |
| 467 class DebuggerStackElement extends ObservatoryElement { | 636 class DebuggerStackElement extends ObservatoryElement { |
| 468 @published Isolate isolate; | 637 @published Isolate isolate; |
| 469 @observable bool hasStack = false; | 638 @observable bool hasStack = false; |
| 470 @observable bool isSampled = false; | 639 @observable bool isSampled = false; |
| 471 Debugger debugger = null; | 640 ObservatoryDebugger debugger; |
| 472 | 641 |
| 473 _addFrame(List frameList, ObservableMap frameInfo, bool expand) { | 642 _addFrame(List frameList, ObservableMap frameInfo, bool expand) { |
| 474 DebuggerFrameElement frameElement = new Element.tag('debugger-frame'); | 643 DebuggerFrameElement frameElement = new Element.tag('debugger-frame'); |
| 475 frameElement.expand = expand; | 644 frameElement.expand = expand; |
| 476 frameElement.frame = frameInfo; | 645 frameElement.frame = frameInfo; |
| 477 | 646 |
| 478 var li = new LIElement(); | 647 var li = new LIElement(); |
| 479 li.classes.add('list-group-item'); | 648 li.classes.add('list-group-item'); |
| 480 li.children.insert(0, frameElement); | 649 li.children.insert(0, frameElement); |
| 481 | 650 |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 645 var br = new BRElement(); | 814 var br = new BRElement(); |
| 646 $['consoleText'].children.add(br); | 815 $['consoleText'].children.add(br); |
| 647 br.scrollIntoView(); | 816 br.scrollIntoView(); |
| 648 } | 817 } |
| 649 } | 818 } |
| 650 | 819 |
| 651 @CustomTag('debugger-input') | 820 @CustomTag('debugger-input') |
| 652 class DebuggerInputElement extends ObservatoryElement { | 821 class DebuggerInputElement extends ObservatoryElement { |
| 653 @published Isolate isolate; | 822 @published Isolate isolate; |
| 654 @published String text = ''; | 823 @published String text = ''; |
| 655 @observable Debugger debugger; | 824 @observable ObservatoryDebugger debugger; |
| 825 @observable bool busy = false; |
| 826 |
| 827 void _notBusy() { |
| 828 busy = false; |
| 829 } |
| 656 | 830 |
| 657 @override | 831 @override |
| 658 void ready() { | 832 void ready() { |
| 659 super.ready(); | 833 super.ready(); |
| 660 var textBox = $['textBox']; | 834 var textBox = $['textBox']; |
| 661 textBox.select(); | 835 textBox.select(); |
| 662 textBox.onKeyDown.listen((KeyboardEvent e) { | 836 textBox.onKeyDown.listen((KeyboardEvent e) { |
| 837 // TODO(turnidge): Ignore *all* key events while busy. |
| 663 switch (e.keyCode) { | 838 switch (e.keyCode) { |
| 664 case KeyCode.TAB: | 839 case KeyCode.TAB: |
| 665 e.preventDefault(); | 840 e.preventDefault(); |
| 666 int cursorPos = textBox.selectionStart; | 841 if (!busy) { |
| 667 var completion = debugger.complete(text.substring(0, cursorPos)); | 842 busy = true; |
| 668 text = completion + text.substring(cursorPos); | 843 int cursorPos = textBox.selectionStart; |
| 669 // TODO(turnidge): Move the cursor to the end of the | 844 debugger.complete(text.substring(0, cursorPos)).then((completion)
{ |
| 670 // completion, rather than the end of the string. | 845 text = completion + text.substring(cursorPos); |
| 846 // TODO(turnidge): Move the cursor to the end of the |
| 847 // completion, rather than the end of the string. |
| 848 }).whenComplete(_notBusy); |
| 849 } |
| 671 break; | 850 break; |
| 672 case KeyCode.ENTER: | 851 case KeyCode.ENTER: |
| 673 if (!debugger.busy) { | 852 if (!busy) { |
| 674 debugger.run(text); | 853 busy = true; |
| 854 var command = text; |
| 675 text = ''; | 855 text = ''; |
| 856 debugger.run(command).whenComplete(_notBusy); |
| 676 } | 857 } |
| 677 break; | 858 break; |
| 678 } | 859 } |
| 679 }); | 860 }); |
| 680 } | 861 } |
| 681 | 862 |
| 682 DebuggerInputElement.created() : super.created(); | 863 DebuggerInputElement.created() : super.created(); |
| 683 } | 864 } |
| 684 | 865 |
| OLD | NEW |