From 076fb7033583c3796b638fdacc24e86ac5edb957 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sun, 22 Dec 2013 15:42:41 -0800 Subject: [PATCH] Adding/removing breakpoints. --- debugger/assets/styles/app.css | 9 +++ debugger/assets/ui/code/function-view.js | 54 ++++++++++++- debugger/src/datasources.js | 39 ++++++++++ debugger/src/session.js | 98 +++++++++++++++++++++++- 4 files changed, 195 insertions(+), 5 deletions(-) diff --git a/debugger/assets/styles/app.css b/debugger/assets/styles/app.css index aa248cc5b..14d7fca14 100644 --- a/debugger/assets/styles/app.css +++ b/debugger/assets/styles/app.css @@ -238,6 +238,15 @@ body { width: 30px; } .debugger-fnview-gutter-icon-el { + padding-left: 7px; + position: relative; + top: -6px; + display: inline-block; + -webkit-font-smoothing: antialiased; + font-style: normal; + font-weight: normal; + line-height: 1; + font-size: 28px; } .debugger-fnview-gutter-addr { width: 70px; diff --git a/debugger/assets/ui/code/function-view.js b/debugger/assets/ui/code/function-view.js index 3428b54ba..284066bc2 100644 --- a/debugger/assets/ui/code/function-view.js +++ b/debugger/assets/ui/code/function-view.js @@ -16,7 +16,7 @@ var module = angular.module('xe.ui.code.functionView', [ module.controller('FunctionViewController', function( - $rootScope, $scope, app, log) { + $rootScope, $scope, app, log, Breakpoint) { $scope.codeType = 'source'; function refresh() { @@ -93,6 +93,8 @@ module.controller('FunctionViewController', function( el.classList.add('debugger-fnview-gutter-code-el'); el.innerText = hex32(line[2]); cm.setGutterMarker(n, 'debugger-fnview-gutter-code', el); + + updateLineIcon(n, line); } } }; @@ -138,11 +140,57 @@ module.controller('FunctionViewController', function( }; $scope.$watch('codeType', updateCode); + function updateLineIcon(line, sourceLine) { + var cm = $scope.codeMirror; + if (sourceLine[0] != 'i') { + return; + } + var address = sourceLine[1]; + var breakpoint = app.session.breakpoints[address]; + var el; + if (breakpoint) { + el = document.createElement('span'); + el.classList.add('debugger-fnview-gutter-icon-el'); + if (breakpoint.enabled) { + el.innerHTML = '●'; + } else { + el.innerHTML = '◌'; + } + } else { + el = null; + } + cm.setGutterMarker(line, 'debugger-fnview-gutter-icon', el); + }; + + function toggleBreakpoint(line, sourceLine, shiftKey) { + var address = sourceLine[1]; + var breakpoint = app.session.breakpoints[address]; + if (breakpoint) { + // Existing breakpoint - toggle or remove. + if (shiftKey || !breakpoint.enabled) { + app.session.toggleBreakpoint(breakpoint, !breakpoint.enabled); + } else { + app.session.removeBreakpoint(breakpoint); + } + } else { + // New breakpoint needed. + breakpoint = app.session.addCodeBreakpoint(address); + } + + updateLineIcon(line, sourceLine); + }; + $scope.codeMirror.on('gutterClick', function( instance, line, gutterClass, e) { if (e.which == 1) { - if (gutterClass == 'debugger-fnview-gutter-icon') { - console.log('click', e); + if (gutterClass == 'debugger-fnview-gutter-icon' || + gutterClsas == 'debugger-fnview-gutter-addr') { + var sourceLine = $scope.sourceLines[line]; + if (!sourceLine || sourceLine[0] != 'i') { + return; + } + e.preventDefault(); + toggleBreakpoint(line, sourceLine, e.shiftKey); } } }); diff --git a/debugger/src/datasources.js b/debugger/src/datasources.js index 8eca0246d..edeea46b6 100644 --- a/debugger/src/datasources.js +++ b/debugger/src/datasources.js @@ -12,6 +12,45 @@ var module = angular.module('xe.datasources', []); +module.service('Breakpoint', function() { + // http://stackoverflow.com/a/2117523/377392 + var uuidFormat = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; + function uuid4() { + return uuidFormat.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); + }; + + var Breakpoint = function(opt_id) { + this.id = opt_id || uuid4(); + this.type = Breakpoint.Type.TEMP; + this.address = 0; + this.enabled = true; + }; + Breakpoint.Type = { + TEMP: 'temp', + CODE: 'code' + }; + Breakpoint.fromJSON = function(json) { + var breakpoint = new Breakpoint(json.id); + breakpoint.type = json.type; + breakpoint.address = json.address; + breakpoint.enabled = json.enabled; + return breakpoint; + }; + Breakpoint.prototype.toJSON = function() { + return { + 'id': this.id, + 'type': this.type, + 'address': this.address, + 'enabled': this.enabled + }; + }; + return Breakpoint; +}); + + module.service('DataSource', function($q) { var DataSource = function(source) { this.source = source; diff --git a/debugger/src/session.js b/debugger/src/session.js index 820837cbe..e79350cb0 100644 --- a/debugger/src/session.js +++ b/debugger/src/session.js @@ -13,17 +13,50 @@ var module = angular.module('xe.session', []); module.service('Session', function( - $rootScope, $q, $http, log, FileDataSource, RemoteDataSource) { + $rootScope, $q, $http, log, + Breakpoint, FileDataSource, RemoteDataSource) { var Session = function(id, opt_dataSource) { this.id = id; + this.breakpoints = {}; + this.dataSource = opt_dataSource || null; + + this.loadState(); }; Session.prototype.dispose = function() { + this.saveState(); this.disconnect(); }; + Session.prototype.loadState = function() { + var json = JSON.parse(window.localStorage[this.id]); + if (!json) { + return; + } + + var breakpointList = json.breakpoints; + this.breakpoints = {}; + for (var n = 0; n < breakpointList.length; n++) { + var breakpointJson = breakpointList[n]; + this.breakpoints[breakpointJson.address] = + Breakpoint.fromJSON(breakpointJson); + } + }; + + Session.prototype.saveState = function() { + var json = { + id: this.id, + breakpoints: [] + }; + for (var key in this.breakpoints) { + var breakpoint = this.breakpoints[key]; + json.breakpoints.push(breakpoint.toJSON()); + } + window.localStorage[this.id] = JSON.stringify(json); + }; + Session.DEFAULT_HOST = '127.0.0.1:6200'; Session.getHost = function(opt_host) { @@ -67,7 +100,7 @@ module.service('Session', function( p.then((function() { log.info('Connected!'); log.clearProgress(); - this.dataSource = dataSource; + this.setDataSource(dataSource); d.resolve(this); }).bind(this), (function(e) { log.error('Unable to connect: ' + e); @@ -82,11 +115,72 @@ module.service('Session', function( }; Session.prototype.disconnect = function() { + this.setDataSource(null); + }; + + Session.prototype.setDataSource = function(dataSource) { if (this.dataSource) { this.dataSource.dispose(); this.dataSource = null; $rootScope.$emit('refresh'); } + if (!dataSource) { + return; + } + + this.dataSource = dataSource; + + var breakpointList = []; + for (var key in this.breakpoints) { + breakpointList.push(this.breakpoints[key]); + } + this.dataSource.addBreakpoints(breakpointList); + }; + + Session.prototype.addBreakpoint = function(breakpoint) { + this.breakpoints[breakpoint.address] = breakpoint; + if (this.dataSource) { + this.dataSource.addBreakpoint(breakpoint); + } + this.saveState(); + return breakpoint; + }; + + Session.prototype.addTempBreakpoint = function(address) { + var breakpoint = new Breakpoint(); + breakpoint.type = Breakpoint.Type.TEMP; + breakpoint.address = address; + breakpoint.enabled = true; + return this.addBreakpoint(breakpoint); + }; + + Session.prototype.addCodeBreakpoint = function(address) { + var breakpoint = new Breakpoint(); + breakpoint.type = Breakpoint.Type.CODE; + breakpoint.address = address; + breakpoint.enabled = true; + return this.addBreakpoint(breakpoint); + }; + + Session.prototype.removeBreakpoint = function(breakpoint) { + delete this.breakpoints[breakpoint.address]; + if (this.dataSource) { + this.dataSource.removeBreakpoint(breakpoint.id); + } + this.saveState(); + }; + + Session.prototype.toggleBreakpoint = function(breakpoint, enabled) { + var oldEnabled = enabled; + breakpoint.enabled = enabled; + if (this.dataSource) { + if (breakpoint.enabled) { + this.dataSource.addBreakpoint(breakpoint); + } else { + this.dataSource.removeBreakpoint(breakpoint.id); + } + } + this.saveState(); }; return Session;