Bug 812618 - Autocomplete at cursor location; r=robcee

This commit is contained in:
Mihai Sucan 2013-06-28 20:56:33 +03:00
parent a49614b2cc
commit d99153a1a6
5 changed files with 112 additions and 16 deletions

View File

@ -279,5 +279,50 @@ function popupHideAfterReturnWithNoSelection()
is(jsterm.history[jsterm.history.length-1], "window.testBug",
"jsterm history is correct");
executeSoon(finishTest);
executeSoon(testCompletionInText);
}
function testCompletionInText()
{
info("test that completion works inside text, see bug 812618");
popup._panel.addEventListener("popupshown", function onShown() {
popup._panel.removeEventListener("popupshown", onShown);
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 2, "popup.itemCount is correct");
EventUtils.synthesizeKey("VK_DOWN", {});
EventUtils.synthesizeKey("VK_DOWN", {});
is(popup.selectedIndex, 0, "popup.selectedIndex is correct");
ok(!completeNode.value, "completeNode.value is empty");
let items = popup.getItems().reverse().map(e => e.label);
let sameItems = items.every((prop, index) =>
["testBug873250a", "testBug873250b"][index] === prop);
ok(sameItems, "getItems returns the items we expect");
info("press Tab and wait for popup to hide");
popup._panel.addEventListener("popuphidden", popupHideAfterCompletionInText);
EventUtils.synthesizeKey("VK_TAB", {});
});
jsterm.setInputValue("dump(window.testBu)");
inputNode.selectionStart = inputNode.selectionEnd = 18;
EventUtils.synthesizeKey("g", {});
}
function popupHideAfterCompletionInText()
{
// At this point the completion suggestion should be accepted.
popup._panel.removeEventListener("popuphidden", popupHideAfterCompletionInText);
ok(!popup.isOpen, "popup is not open");
is(inputNode.value, "dump(window.testBug873250b)",
"completion was successful after VK_TAB");
is(inputNode.selectionStart, 26, "cursor location is correct");
is(inputNode.selectionStart, inputNode.selectionEnd, "cursor location (confirmed)");
ok(!completeNode.value, "completeNode is empty");
finishTest();
}

View File

@ -4077,9 +4077,8 @@ JSTerm.prototype = {
return false;
}
// Only complete if the selection is empty and at the end of the input.
if (inputNode.selectionStart == inputNode.selectionEnd &&
inputNode.selectionEnd != inputValue.length) {
// Only complete if the selection is empty.
if (inputNode.selectionStart != inputNode.selectionEnd) {
this.clearCompletion();
return false;
}
@ -4215,6 +4214,11 @@ JSTerm.prototype = {
onAutocompleteSelect: function JSTF_onAutocompleteSelect()
{
// Render the suggestion only if the cursor is at the end of the input.
if (this.inputNode.selectionStart != this.inputNode.value.length) {
return;
}
let currentItem = this.autocompletePopup.selectedItem;
if (currentItem && this.lastCompletion.value) {
let suffix = currentItem.label.substring(this.lastCompletion.
@ -4256,7 +4260,11 @@ JSTerm.prototype = {
if (currentItem && this.lastCompletion.value) {
let suffix = currentItem.label.substring(this.lastCompletion.
matchProp.length);
this.setInputValue(this.inputNode.value + suffix);
let cursor = this.inputNode.selectionStart;
let value = this.inputNode.value;
this.setInputValue(value.substr(0, cursor) + suffix + value.substr(cursor));
let newCursor = cursor + suffix.length;
this.inputNode.selectionStart = this.inputNode.selectionEnd = newCursor;
updated = true;
}

View File

@ -626,7 +626,8 @@ WebConsoleActor.prototype =
{
// TODO: Bug 842682 - use the debugger API for autocomplete in the Web
// Console, and provide suggestions from the selected debugger stack frame.
let result = JSPropertyProvider(this.window, aRequest.text) || {};
let result = JSPropertyProvider(this.window, aRequest.text,
aRequest.cursor) || {};
return {
from: this.actorID,
matches: result.matches || [],

View File

@ -727,10 +727,12 @@ function findCompletionBeginning(aStr)
*
* @param object aScope
* Scope to use for the completion.
*
* @param string aInputValue
* Value that should be completed.
*
* @param number [aCursor=aInputValue.length]
* Optional offset in the input where the cursor is located. If this is
* omitted then the cursor is assumed to be at the end of the input
* value.
* @returns null or object
* If no completion valued could be computed, null is returned,
* otherwise a object with the following form is returned:
@ -740,13 +742,18 @@ function findCompletionBeginning(aStr)
* the matches-strings.
* }
*/
function JSPropertyProvider(aScope, aInputValue)
function JSPropertyProvider(aScope, aInputValue, aCursor)
{
if (aCursor === undefined) {
aCursor = aInputValue.length;
}
let inputValue = aInputValue.substring(0, aCursor);
let obj = WCU.unwrap(aScope);
// Analyse the aInputValue and find the beginning of the last part that
// Analyse the inputValue and find the beginning of the last part that
// should be completed.
let beginning = findCompletionBeginning(aInputValue);
let beginning = findCompletionBeginning(inputValue);
// There was an error analysing the string.
if (beginning.err) {
@ -759,7 +766,7 @@ function JSPropertyProvider(aScope, aInputValue)
return null;
}
let completionPart = aInputValue.substring(beginning.startPos);
let completionPart = inputValue.substring(beginning.startPos);
// Don't complete on just an empty string.
if (completionPart.trim() == "") {

View File

@ -37,15 +37,16 @@ function onAttach(aState, aResponse)
gState = aState;
let tests = [doAutocomplete1, doAutocomplete2, doSimpleEval, doWindowEval,
doEvalWithException, doEvalWithHelper, doEvalString, doEvalLongString];
let tests = [doAutocomplete1, doAutocomplete2, doAutocomplete3,
doAutocomplete4, doSimpleEval, doWindowEval, doEvalWithException,
doEvalWithHelper, doEvalString, doEvalLongString];
runTests(tests, testEnd);
}
function doAutocomplete1()
{
info("test autocomplete for 'window.foo'");
gState.client.autocomplete("window.foo", 0, onAutocomplete1);
gState.client.autocomplete("window.foo", 10, onAutocomplete1);
}
function onAutocomplete1(aResponse)
@ -62,7 +63,7 @@ function onAutocomplete1(aResponse)
function doAutocomplete2()
{
info("test autocomplete for 'window.foobarObject.'");
gState.client.autocomplete("window.foobarObject.", 0, onAutocomplete2);
gState.client.autocomplete("window.foobarObject.", 20, onAutocomplete2);
}
function onAutocomplete2(aResponse)
@ -77,6 +78,40 @@ function onAutocomplete2(aResponse)
nextTest();
}
function doAutocomplete3()
{
// Check that completion suggestions are offered inside the string.
info("test autocomplete for 'dump(window.foobarObject.)'");
gState.client.autocomplete("dump(window.foobarObject.)", 25, onAutocomplete3);
}
function onAutocomplete3(aResponse)
{
let matches = aResponse.matches;
ok(!aResponse.matchProp, "matchProp");
is(matches.length, 7, "matches.length");
checkObject(matches,
["foo", "foobar", "foobaz", "omg", "omgfoo", "omgstr", "strfoo"]);
nextTest();
}
function doAutocomplete4()
{
// Check that completion requests can have no suggestions.
info("test autocomplete for 'dump(window.foobarObject.)'");
gState.client.autocomplete("dump(window.foobarObject.)", 26, onAutocomplete4);
}
function onAutocomplete4(aResponse)
{
ok(!aResponse.matchProp, "matchProp");
is(aResponse.matches.length, 0, "matches.length");
nextTest();
}
function doSimpleEval()
{
info("test eval '2+2'");