Bug 886848 - Convert the editor to rely more on Promises, r=past

This commit is contained in:
Victor Porof 2013-09-13 16:23:13 +03:00
Родитель 472bc667b0
Коммит d647e74fa5
4 изменённых файлов: 98 добавлений и 115 удалений

Просмотреть файл

@ -453,7 +453,6 @@ StackFrames.prototype = {
currentEvaluation: null,
currentException: null,
currentReturnedValue: null,
_dontSwitchSources: false,
/**
* Connect to the current thread client.
@ -684,7 +683,6 @@ StackFrames.prototype = {
*/
_onBlackBoxChange: function() {
if (this.activeThread.state == "paused") {
this._dontSwitchSources = true;
this.currentFrame = null;
this._refillFrames();
}
@ -727,11 +725,8 @@ StackFrames.prototype = {
return;
}
let noSwitch = this._dontSwitchSources;
this._dontSwitchSources = false;
// Move the editor's caret to the proper url and line.
DebuggerView.updateEditor(where.url, where.line, { noSwitch: noSwitch });
DebuggerView.setEditorLocation(where.url, where.line);
// Highlight the breakpoint at the specified url and line if it exists.
DebuggerView.Sources.highlightBreakpoint(where, { noEditorUpdate: true });
// Don't display the watch expressions textbox inputs in the pane.
@ -741,7 +736,6 @@ StackFrames.prototype = {
// Clear existing scopes and create each one dynamically.
DebuggerView.Variables.empty();
// If watch expressions evaluation results are available, create a scope
// to contain all the values.
if (this.syncedWatchExpressions && aDepth == 0) {
@ -942,6 +936,7 @@ SourceScripts.prototype = {
get activeThread() DebuggerController.activeThread,
get debuggerClient() DebuggerController.client,
_newSourceTimeout: null,
_cache: new Map(),
/**
* Connect to the current thread client.
@ -980,6 +975,7 @@ SourceScripts.prototype = {
// Retrieve the list of script sources known to the server from before
// the client was ready to handle "newSource" notifications.
this._cache.clear();
this.activeThread.getSources(this._onSourcesAdded);
},
@ -1123,12 +1119,13 @@ SourceScripts.prototype = {
*/
getTextForSource: function(aSource, aOnTimeout, aDelay = FETCH_SOURCE_RESPONSE_DELAY) {
// Fetch the source text only once.
if (aSource._fetched) {
return aSource._fetched;
let textPromise = this._cache.get(aSource.url);
if (textPromise) {
return textPromise;
}
let deferred = promise.defer();
aSource._fetched = deferred.promise;
this._cache.set(aSource.url, deferred.promise);
// If the source text takes a long time to fetch, invoke a callback.
if (aOnTimeout) {

Просмотреть файл

@ -350,7 +350,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
// Update the editor location if necessary.
if (!aOptions.noEditorUpdate) {
DebuggerView.updateEditor(aLocation.url, aLocation.line, { noDebug: true });
DebuggerView.setEditorLocation(aLocation.url, aLocation.line, { noDebug: true });
}
// If the breakpoint requires a new conditional expression, display
@ -648,12 +648,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
return;
}
// The container is not empty and an actual item was selected.
let selectedSource = sourceItem.attachment.source;
if (DebuggerView.editorSource != selectedSource) {
DebuggerView.editorSource = selectedSource;
}
DebuggerView.setEditorLocation(sourceItem.value);
this.maybeShowBlackBoxMessage();
},
@ -662,9 +657,9 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
* selected source is black boxed or not.
*/
maybeShowBlackBoxMessage: function () {
const source = DebuggerController.activeThread.source(
DebuggerView.editorSource);
this._editorDeck.selectedIndex = source.isBlackBoxed ? 1 : 0;
let sourceForm = this.selectedItem.attachment.source;
let sourceClient = DebuggerController.activeThread.source(sourceForm);
this._editorDeck.selectedIndex = sourceClient.isBlackBoxed ? 1 : 0;
},
/**
@ -1799,7 +1794,7 @@ GlobalSearchView.prototype = Heritage.extend(WidgetMethods, {
let location = sourceResultsItem.location;
let lineNumber = lineResultsItem.lineNumber;
DebuggerView.updateEditor(location, lineNumber + 1, { noDebug: true });
DebuggerView.setEditorLocation(location, lineNumber + 1, { noDebug: true });
let editor = DebuggerView.editor;
let offset = editor.getCaretOffset();

Просмотреть файл

@ -1390,7 +1390,7 @@ FilteredSourcesView.prototype = Heritage.extend(ResultsPanelContainer.prototype,
*/
_onSelect: function({ detail: locationItem }) {
if (locationItem) {
DebuggerView.updateEditor(locationItem.attachment.fullValue, 0);
DebuggerView.setEditorLocation(locationItem.attachment.fullValue, 0);
}
}
});
@ -1613,7 +1613,7 @@ FilteredFunctionsView.prototype = Heritage.extend(ResultsPanelContainer.prototyp
let scriptOffset = functionItem.attachment.scriptOffset;
let actualLocation = functionItem.attachment.actualLocation;
DebuggerView.updateEditor(sourceUrl, actualLocation.start.line, {
DebuggerView.setEditorLocation(sourceUrl, actualLocation.start.line, {
charOffset: scriptOffset,
columnOffset: actualLocation.start.column,
noDebug: true

Просмотреть файл

@ -27,6 +27,13 @@ const SEARCH_FUNCTION_FLAG = "@";
const SEARCH_TOKEN_FLAG = "#";
const SEARCH_LINE_FLAG = ":";
const SEARCH_VARIABLE_FLAG = "*";
const DEFAULT_EDITOR_CONFIG = {
mode: SourceEditor.MODES.TEXT,
readOnly: true,
showLineNumbers: true,
showAnnotationRuler: true,
showOverviewRuler: true
};
Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
@ -167,17 +174,8 @@ let DebuggerView = {
_initializeEditor: function(aCallback) {
dumpn("Initializing the DebuggerView editor");
let placeholder = document.getElementById("editor");
let config = {
mode: SourceEditor.MODES.JAVASCRIPT,
readOnly: true,
showLineNumbers: true,
showAnnotationRuler: true,
showOverviewRuler: true
};
this.editor = new SourceEditor();
this.editor.init(placeholder, config, () => {
this.editor.init(document.getElementById("editor"), DEFAULT_EDITOR_CONFIG, () => {
this._loadingText = L10N.getStr("loadingText");
this._onEditorLoad(aCallback);
});
@ -215,6 +213,19 @@ let DebuggerView = {
});
},
/**
* Sets the currently displayed text contents in the source editor.
* This resets the mode and undo stack.
*
* @param string aTextContent
* The source text content.
*/
_setEditorText: function(aTextContent = "") {
this.editor.setMode(SourceEditor.MODES.TEXT);
this.editor.setText(aTextContent);
this.editor.resetUndo();
},
/**
* Sets the proper editor mode (JS or HTML) according to the specified
* content type, or by determining the type from the url or text content.
@ -226,7 +237,7 @@ let DebuggerView = {
* @param string aTextContent [optional]
* The source text content.
*/
setEditorMode: function(aUrl, aContentType = "", aTextContent = "") {
_setEditorMode: function(aUrl, aContentType = "", aTextContent = "") {
// Avoid setting the editor mode for very large files.
if (aTextContent.length >= SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
this.editor.setMode(SourceEditor.MODES.TEXT);
@ -256,136 +267,115 @@ let DebuggerView = {
/**
* Sets the currently displayed source text in the editor.
*
* To update the source editor's current caret and debug location based on
* a requested url and line, use the DebuggerView.updateEditor method.
* You should use DebuggerView.updateEditor instead. It updates the current
* caret and debug location based on a requested url and line.
*
* @param object aSource
* The source object coming from the active thread.
* @return object
* A promise that is resolved after the source text has been set.
*/
set editorSource(aSource) {
if (!this._isInitialized || this._isDestroyed || this._editorSource == aSource) {
return;
_setEditorSource: function(aSource) {
// Avoid setting the same source text in the editor again.
if (this._editorSource.url == aSource.url) {
return this._editorSource.promise;
}
dumpn("Setting the DebuggerView editor source: " + aSource.url +
", fetched: " + !!aSource._fetched);
let deferred = promise.defer();
this.editor.setMode(SourceEditor.MODES.TEXT);
this.editor.setText(L10N.getStr("loadingText"));
this.editor.resetUndo();
this._editorSource = aSource;
this._setEditorText(L10N.getStr("loadingText"));
this._editorSource = { url: aSource.url, promise: deferred.promise };
DebuggerController.SourceScripts.getTextForSource(aSource).then(([, aText]) => {
// Avoid setting an unexpected source. This may happen when fast switching
// between sources that haven't been fetched yet.
if (this._editorSource != aSource) {
// Avoid setting an unexpected source. This may happen when switching
// very fast between sources that haven't been fetched yet.
if (this._editorSource.url != aSource.url) {
return;
}
this.editor.setText(aText);
this.editor.resetUndo();
this.setEditorMode(aSource.url, aSource.contentType, aText);
// Update the editor's current caret and debug locations given by the
// currently active frame in the stack, if there's one available.
this.updateEditor();
this._setEditorText(aText);
this._setEditorMode(aSource.url, aSource.contentType, aText);
// Synchronize any other components with the currently displayed source.
DebuggerView.Sources.selectedValue = aSource.url;
DebuggerController.Breakpoints.updateEditorBreakpoints();
// Notify that we've shown a source file.
// Resolve and notify that a source file was shown.
window.dispatchEvent(document, "Debugger:SourceShown", aSource);
deferred.resolve([aSource, aText]);
},
([, aError]) => {
// Rejected.
let msg = L10N.getStr("errorLoadingText") + DevToolsUtils.safeErrorString(aError);
this.editor.setText(msg);
window.dispatchEvent(document, "Debugger:SourceErrorShown", aError);
dumpn(msg);
this._setEditorText(msg);
Cu.reportError(msg);
dumpn(msg);
// Reject and notify that there was an error showing the source file.
window.dispatchEvent(document, "Debugger:SourceErrorShown", aError);
deferred.reject([aSource, aError]);
});
return deferred.promise;
},
/**
* Gets the currently displayed source text in the editor.
*
* @return object
* The source object coming from the active thread.
*/
get editorSource() this._editorSource,
/**
* Update the source editor's current caret and debug location based on
* a requested url and line. If unspecified, they default to the location
* given by the currently active frame in the stack.
* a requested url and line.
*
* @param string aUrl [optional]
* @param string aUrl
* The target source url.
* @param number aLine [optional]
* The target line number in the source.
* The target line in the source.
* @param object aFlags [optional]
* Additional options for showing the source. Supported options:
* - charOffset: character offset for the caret or debug location
* - lineOffset: line offset for the caret or debug location
* - columnOffset: column offset for the caret or debug location
* - noSwitch: don't switch to the source if not currently selected
* - noCaret: don't set the caret location at the specified line
* - noDebug: don't set the debug location at the specified line
* @return object
* A promise that is resolved after the source text has been set.
*/
updateEditor: function(aUrl, aLine, aFlags = {}) {
if (!this._isInitialized || this._isDestroyed) {
return;
setEditorLocation: function(aUrl, aLine = 0, aFlags = {}) {
// Avoid trying to set a source for a url that isn't known yet.
if (!this.Sources.containsValue(aUrl)) {
return promise.reject(new Error("Unknown source for the specified URL."));
}
// If the location is not specified, default to the location given by
// the currently active frame in the stack.
if (!aUrl && !aLine) {
// If the line is not specified, default to the current frame's position,
// if available and the frame's url corresponds to the requested url.
if (!aLine) {
let cachedFrames = DebuggerController.activeThread.cachedFrames;
let currentFrame = DebuggerController.StackFrames.currentFrame;
let frame = cachedFrames[currentFrame];
if (frame) {
let { url, line } = frame.where;
this.updateEditor(url, line, { noSwitch: true });
let currentDepth = DebuggerController.StackFrames.currentFrameDepth;
let frame = cachedFrames[currentDepth];
if (frame && frame.where.url == aUrl) {
aLine = frame.where.line;
}
return;
}
dumpn("Updating the DebuggerView editor: " + aUrl + " @ " + aLine +
", flags: " + aFlags.toSource());
let sourceItem = this.Sources.getItemByValue(aUrl);
let sourceClient = sourceItem.attachment.source;
// If the currently displayed source is the requested one, update.
if (this.Sources.selectedValue == aUrl) {
set(aLine);
}
// If the requested source exists, display it and update.
else if (this.Sources.containsValue(aUrl) && !aFlags.noSwitch) {
this.Sources.selectedValue = aUrl;
set(aLine);
}
// Dumb request, invalidate the caret position and debug location.
else {
set(0);
}
// Updates the source editor's caret position and debug location.
// @param number a Line
function set(aLine) {
let editor = DebuggerView.editor;
// Handle any additional options for showing the source.
// Make sure the requested source client is shown in the editor, then
// update the source editor's caret position and debug location.
return this._setEditorSource(sourceClient).then(() => {
// Line numbers in the source editor should start from 1. If invalid
// or not specified, then don't do anything.
if (aLine < 1) {
return;
}
if (aFlags.charOffset) {
aLine += editor.getLineAtOffset(aFlags.charOffset);
aLine += this.editor.getLineAtOffset(aFlags.charOffset);
}
if (aFlags.lineOffset) {
aLine += aFlags.lineOffset;
}
if (!aFlags.noCaret) {
editor.setCaretPosition(aLine - 1, aFlags.columnOffset);
this.editor.setCaretPosition(aLine - 1, aFlags.columnOffset);
}
if (!aFlags.noDebug) {
editor.setDebugLocation(aLine - 1, aFlags.columnOffset);
this.editor.setDebugLocation(aLine - 1, aFlags.columnOffset);
}
}
});
},
/**
@ -478,9 +468,10 @@ let DebuggerView = {
this.Variables.empty();
if (this.editor) {
this.editor.setMode(SourceEditor.MODES.TEXT);
this.editor.setText("");
this.editor.focus();
this._editorSource = null;
this.editor.resetUndo();
this._editorSource = {};
}
},
@ -497,8 +488,8 @@ let DebuggerView = {
Sources: null,
Variables: null,
WatchExpressions: null,
_editor: null,
_editorSource: null,
editor: null,
_editorSource: {},
_loadingText: "",
_sourcesPane: null,
_instrumentsPane: null,