mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 852931 - cmd+click jumps to function defintion in the debugger. r=jlongster
This commit is contained in:
parent
8b5747c4b8
commit
f46b45a818
@ -31,6 +31,7 @@ const SEARCH_AUTOFILL = [SEARCH_GLOBAL_FLAG, SEARCH_FUNCTION_FLAG, SEARCH_TOKEN_
|
||||
const EDITOR_VARIABLE_HOVER_DELAY = 750; // ms
|
||||
const EDITOR_VARIABLE_POPUP_POSITION = "topcenter bottomleft";
|
||||
const TOOLBAR_ORDER_POPUP_POSITION = "topcenter bottomleft";
|
||||
const FUNCTION_SEARCH_POPUP_POSITION = "topcenter bottomleft";
|
||||
const RESIZE_REFRESH_RATE = 50; // ms
|
||||
const PROMISE_DEBUGGER_URL =
|
||||
"chrome://devtools/content/promisedebugger/promise-debugger.xhtml";
|
||||
|
@ -18,6 +18,7 @@ support-files =
|
||||
code_breakpoints-other-tabs.js
|
||||
code_bug-896139.js
|
||||
code_frame-script.js
|
||||
code_function-jump-01.js
|
||||
code_function-search-01.js
|
||||
code_function-search-02.js
|
||||
code_function-search-03.js
|
||||
@ -69,6 +70,7 @@ support-files =
|
||||
doc_event-listeners-04.html
|
||||
doc_frame-parameters.html
|
||||
doc_function-display-name.html
|
||||
doc_function-jump.html
|
||||
doc_function-search.html
|
||||
doc_global-method-override.html
|
||||
doc_iframes.html
|
||||
@ -257,6 +259,8 @@ skip-if = e10s # TODO
|
||||
skip-if = e10s
|
||||
[browser_dbg_host-layout.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_jump-to-function-definition.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_iframes.js]
|
||||
skip-if = e10s # TODO
|
||||
[browser_dbg_instruments-pane-collapse.js]
|
||||
|
@ -0,0 +1,45 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the jump to function definition works properly.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_function-jump.html";
|
||||
const SCRIPT_URI = EXAMPLE_URL + "code_function-jump-01.js";
|
||||
|
||||
|
||||
function test() {
|
||||
let gTab, gPanel, gDebugger, gSources;
|
||||
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
|
||||
waitForSourceShown(gPanel, "-01.js")
|
||||
.then(jumpToFunctionDefinition)
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
});
|
||||
|
||||
function jumpToFunctionDefinition() {
|
||||
let callLocation = {line: 5, ch: 0};
|
||||
let editor = gDebugger.DebuggerView.editor;
|
||||
let coords = editor.getCoordsFromPosition(callLocation);
|
||||
|
||||
gDebugger.DebuggerView.Sources._onMouseDown({ clientX: coords.left,
|
||||
clientY: coords.top,
|
||||
metaKey: true });
|
||||
|
||||
let deferred = promise.defer();
|
||||
executeSoon(() => {
|
||||
is(editor.getDebugLocation(), 1, "foo definition should be highlighted");
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
|
||||
function foo() {
|
||||
//some function
|
||||
}
|
||||
|
||||
foo();
|
@ -0,0 +1,17 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Foo bar, bar, bazz, bar foo bar!</p>
|
||||
|
||||
<script type="text/javascript" src="code_function-jump-01.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -316,6 +316,48 @@ var SourceUtils = {
|
||||
}
|
||||
// Give up.
|
||||
return aUrl.spec;
|
||||
},
|
||||
|
||||
parseSource: function(aDebuggerView, aParser) {
|
||||
let editor = aDebuggerView.editor;
|
||||
|
||||
let contents = editor.getText();
|
||||
let location = aDebuggerView.Sources.selectedValue;
|
||||
let parsedSource = aParser.get(contents, location);
|
||||
|
||||
return parsedSource;
|
||||
},
|
||||
|
||||
findIdentifier: function(aEditor, parsedSource, x, y) {
|
||||
let editor = aEditor;
|
||||
|
||||
// Calculate the editor's line and column at the current x and y coords.
|
||||
let hoveredPos = editor.getPositionFromCoords({ left: x, top: y });
|
||||
let hoveredOffset = editor.getOffset(hoveredPos);
|
||||
let hoveredLine = hoveredPos.line;
|
||||
let hoveredColumn = hoveredPos.ch;
|
||||
|
||||
let scriptInfo = parsedSource.getScriptInfo(hoveredOffset);
|
||||
|
||||
// If the script length is negative, we're not hovering JS source code.
|
||||
if (scriptInfo.length == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Using the script offset, determine the actual line and column inside the
|
||||
// script, to use when finding identifiers.
|
||||
let scriptStart = editor.getPosition(scriptInfo.start);
|
||||
let scriptLineOffset = scriptStart.line;
|
||||
let scriptColumnOffset = (hoveredLine == scriptStart.line ? scriptStart.ch : 0);
|
||||
|
||||
let scriptLine = hoveredLine - scriptLineOffset;
|
||||
let scriptColumn = hoveredColumn - scriptColumnOffset;
|
||||
let identifierInfo = parsedSource.getIdentifierAt({
|
||||
line: scriptLine + 1,
|
||||
column: scriptColumn,
|
||||
scriptIndex: scriptInfo.index
|
||||
});
|
||||
|
||||
return identifierInfo;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,7 @@ function SourcesView(DebuggerController, DebuggerView) {
|
||||
this.Breakpoints = DebuggerController.Breakpoints;
|
||||
this.SourceScripts = DebuggerController.SourceScripts;
|
||||
this.DebuggerView = DebuggerView;
|
||||
this.Parser = DebuggerController.Parser;
|
||||
|
||||
this.togglePrettyPrint = this.togglePrettyPrint.bind(this);
|
||||
this.toggleBlackBoxing = this.toggleBlackBoxing.bind(this);
|
||||
@ -28,6 +29,7 @@ function SourcesView(DebuggerController, DebuggerView) {
|
||||
this._onEditorLoad = this._onEditorLoad.bind(this);
|
||||
this._onEditorUnload = this._onEditorUnload.bind(this);
|
||||
this._onEditorCursorActivity = this._onEditorCursorActivity.bind(this);
|
||||
this._onMouseDown = this._onMouseDown.bind(this);
|
||||
this._onSourceSelect = this._onSourceSelect.bind(this);
|
||||
this._onStopBlackBoxing = this._onStopBlackBoxing.bind(this);
|
||||
this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this);
|
||||
@ -69,10 +71,16 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this._newTabMenuItem = document.getElementById("debugger-sources-context-newtab");
|
||||
this._copyUrlMenuItem = document.getElementById("debugger-sources-context-copyurl");
|
||||
|
||||
this._noResultsFoundToolTip = new Tooltip(document);
|
||||
this._noResultsFoundToolTip.defaultPosition = FUNCTION_SEARCH_POPUP_POSITION;
|
||||
|
||||
if (Prefs.prettyPrintEnabled) {
|
||||
this._prettyPrintButton.removeAttribute("hidden");
|
||||
}
|
||||
|
||||
this._editorContainer = document.getElementById("editor");
|
||||
this._editorContainer.addEventListener("mousedown", this._onMouseDown, false);
|
||||
|
||||
window.on(EVENTS.EDITOR_LOADED, this._onEditorLoad, false);
|
||||
window.on(EVENTS.EDITOR_UNLOADED, this._onEditorUnload, false);
|
||||
this.widget.addEventListener("select", this._onSourceSelect, false);
|
||||
@ -968,6 +976,81 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
aEditor.off("cursorActivity", this._onEditorCursorActivity);
|
||||
},
|
||||
|
||||
_onMouseDown: function(e) {
|
||||
this.hideNoResultsTooltip();
|
||||
|
||||
if (!e.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
let editor = this.DebuggerView.editor;
|
||||
let identifier = this._findIdentifier(e.clientX, e.clientY);
|
||||
|
||||
if (!identifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
let foundDefinitions = this._getFunctionDefinitions(identifier);
|
||||
|
||||
if (!foundDefinitions || !foundDefinitions.definitions) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._showFunctionDefinitionResults(identifier, foundDefinitions.definitions, editor);
|
||||
},
|
||||
|
||||
/**
|
||||
* Searches for function definition of a function in a given source file
|
||||
*/
|
||||
|
||||
_findDefinition: function(parsedSource, aName) {
|
||||
let functionDefinitions = parsedSource.getNamedFunctionDefinitions(aName);
|
||||
|
||||
let resultList = [];
|
||||
|
||||
if (!functionDefinitions || !functionDefinitions.length || !functionDefinitions[0].length) {
|
||||
return {
|
||||
definitions: resultList
|
||||
};
|
||||
}
|
||||
|
||||
//functionDefinitions is a list with an object full of metadata, extract the
|
||||
//data and use to construct a more useful, less cluttered, contextual list
|
||||
for (let i=0; i<functionDefinitions.length; i++) {
|
||||
let functionDefinition = {
|
||||
source: functionDefinitions[i].sourceUrl,
|
||||
startLine: functionDefinitions[i][0].functionLocation.start.line,
|
||||
startColumn: functionDefinitions[i][0].functionLocation.start.column,
|
||||
name: functionDefinitions[i][0].functionName
|
||||
}
|
||||
|
||||
resultList.push(functionDefinition)
|
||||
}
|
||||
|
||||
return {
|
||||
definitions: resultList
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Searches for an identifier underneath the specified position in the
|
||||
* source editor.
|
||||
*
|
||||
* @param number x, y
|
||||
* The left/top coordinates where to look for an identifier.
|
||||
*/
|
||||
_findIdentifier: function(x, y) {
|
||||
let parsedSource = SourceUtils.parseSource(this.DebuggerView, this.Parser);
|
||||
let identifierInfo = SourceUtils.findIdentifier(this.DebuggerView.editor, parsedSource, x, y);
|
||||
|
||||
// Not hovering over an identifier
|
||||
if (!identifierInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
return identifierInfo;
|
||||
},
|
||||
|
||||
/**
|
||||
* The selection listener for the source editor.
|
||||
*/
|
||||
@ -986,6 +1069,60 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Uses function definition data to perform actions in different
|
||||
* cases of how many locations were found: zero, one, or mulitple definitions
|
||||
*/
|
||||
_showFunctionDefinitionResults: function(aHoveredFunction, aDefinitionList, aEditor) {
|
||||
let definitions = aDefinitionList;
|
||||
let hoveredFunction = aHoveredFunction;
|
||||
|
||||
//show a popup saying no results were found
|
||||
if (definitions.length == 0) {
|
||||
this._noResultsFoundToolTip.setTextContent({
|
||||
messages: [L10N.getStr("noMatchingStringsText")]
|
||||
});
|
||||
|
||||
this._markedIdentifier = aEditor.markText(
|
||||
{ line: hoveredFunction.location.start.line - 1, ch: hoveredFunction.location.start.column },
|
||||
{ line: hoveredFunction.location.end.line - 1, ch: hoveredFunction.location.end.column });
|
||||
|
||||
this._noResultsFoundToolTip.show(this._markedIdentifier.anchor);
|
||||
|
||||
} else if(definitions.length == 1) {
|
||||
this.DebuggerView.setEditorLocation(definitions[0].source, definitions[0].startLine);
|
||||
} else {
|
||||
//multiple definitions found, do something else
|
||||
this.DebuggerView.setEditorLocation(definitions[0].source, definitions[0].startLine);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides the tooltip and clear marked text popup.
|
||||
*/
|
||||
hideNoResultsTooltip: function() {
|
||||
this._noResultsFoundToolTip.hide();
|
||||
if (this._markedIdentifier) {
|
||||
this._markedIdentifier.clear();
|
||||
this._markedIdentifier = null;
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Gets the definition locations from function metadata
|
||||
*/
|
||||
_getFunctionDefinitions: function(aIdentifierInfo) {
|
||||
let parsedSource = SourceUtils.parseSource(this.DebuggerView, this.Parser);
|
||||
let definition_info = this._findDefinition(parsedSource, aIdentifierInfo.name);
|
||||
|
||||
//Did not find any definitions for the identifier
|
||||
if (!definition_info) {
|
||||
return;
|
||||
}
|
||||
|
||||
return definition_info;
|
||||
},
|
||||
|
||||
/**
|
||||
* The select listener for the sources container.
|
||||
*/
|
||||
@ -1291,7 +1428,9 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
_cbPanel: null,
|
||||
_cbTextbox: null,
|
||||
_selectedBreakpointItem: null,
|
||||
_conditionalPopupVisible: false
|
||||
_conditionalPopupVisible: false,
|
||||
_noResultsFoundToolTip: null,
|
||||
_markedIdentifier: null
|
||||
});
|
||||
|
||||
DebuggerView.Sources = new SourcesView(DebuggerController, DebuggerView);
|
||||
|
Loading…
Reference in New Issue
Block a user