forked from mirrors/gecko-dev
Bug 1430558 - add closing parenthesis automatically in CSS autocompletes;r=gl
MozReview-Commit-ID: LLBrLC3Bq0t --HG-- extra : rebase_source : c081b53042dcc14c6f7bb022b35edab12a5deff0
This commit is contained in:
parent
695dbf5db9
commit
d06e921eb2
5 changed files with 145 additions and 16 deletions
|
|
@ -83,10 +83,10 @@ const TEST_DATA_INNER = [
|
|||
["u", "style=\"background:unset", 19, 23, true],
|
||||
["r", "style=\"background:url", 20, 21, false],
|
||||
["l", "style=\"background:url", 21, 21, false],
|
||||
["(", "style=\"background:url(", 22, 22, false],
|
||||
["'", "style=\"background:url('", 23, 23, false],
|
||||
["1", "style=\"background:url('1", 24, 24, false],
|
||||
["'", "style=\"background:url('1'", 25, 25, false],
|
||||
["(", "style=\"background:url()", 22, 22, false],
|
||||
["'", "style=\"background:url(')", 23, 23, false],
|
||||
["1", "style=\"background:url('1)", 24, 24, false],
|
||||
["'", "style=\"background:url('1')", 25, 25, false],
|
||||
[")", "style=\"background:url('1')", 26, 26, false],
|
||||
[";", "style=\"background:url('1');", 27, 27, false],
|
||||
[" ", "style=\"background:url('1'); ", 28, 28, false],
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
const Services = require("Services");
|
||||
const focusManager = Services.focus;
|
||||
const {KeyCodes} = require("devtools/client/shared/keycodes");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");
|
||||
|
||||
loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
|
||||
|
||||
|
|
@ -44,8 +46,10 @@ const MAX_POPUP_ENTRIES = 500;
|
|||
const FOCUS_FORWARD = focusManager.MOVEFOCUS_FORWARD;
|
||||
const FOCUS_BACKWARD = focusManager.MOVEFOCUS_BACKWARD;
|
||||
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");
|
||||
const WORD_REGEXP = /\w/;
|
||||
const isWordChar = function(str) {
|
||||
return str && WORD_REGEXP.test(str);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to check if the provided key matches one of the expected keys.
|
||||
|
|
@ -1051,6 +1055,10 @@ InplaceEditor.prototype = {
|
|||
let key = event.keyCode;
|
||||
let input = this.input;
|
||||
|
||||
// We want to autoclose some characters, remember the pressed key in order to process
|
||||
// it later on in maybeSuggestionCompletion().
|
||||
this._pressedKey = event.key;
|
||||
|
||||
let multilineNavigation = !this._isSingleLine() &&
|
||||
isKeyIn(key, "UP", "DOWN", "LEFT", "RIGHT");
|
||||
let isPlainText = this.contentType == CONTENT_TYPES.PLAIN_TEXT;
|
||||
|
|
@ -1293,6 +1301,7 @@ InplaceEditor.prototype = {
|
|||
if (!this.input) {
|
||||
return;
|
||||
}
|
||||
|
||||
let preTimeoutQuery = this.input.value;
|
||||
|
||||
// Since we are calling this method from a keypress event handler, the
|
||||
|
|
@ -1477,12 +1486,58 @@ InplaceEditor.prototype = {
|
|||
} else {
|
||||
this._hideAutocompletePopup();
|
||||
}
|
||||
|
||||
this._autocloseParenthesis();
|
||||
|
||||
// This emit is mainly for the purpose of making the test flow simpler.
|
||||
this.emit("after-suggest");
|
||||
this._doValidation();
|
||||
}, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Automatically add closing parenthesis and skip closing parenthesis when needed.
|
||||
*/
|
||||
_autocloseParenthesis: function() {
|
||||
// Split the current value at the cursor index to rebuild the string.
|
||||
let parts = this._splitStringAt(this.input.value, this.input.selectionStart);
|
||||
|
||||
// Lookup the character following the caret to know if the string should be modified.
|
||||
let nextChar = parts[1][0];
|
||||
|
||||
// Autocomplete closing parenthesis if the last key pressed was "(" and the next
|
||||
// character is not a "word" character.
|
||||
if (this._pressedKey == "(" && !isWordChar(nextChar)) {
|
||||
this._updateValue(parts[0] + ")" + parts[1]);
|
||||
}
|
||||
|
||||
// Skip inserting ")" if the next character is already a ")" (note that we actually
|
||||
// insert and remove the extra ")" here, as the input has already been modified).
|
||||
if (this._pressedKey == ")" && nextChar == ")") {
|
||||
this._updateValue(parts[0] + parts[1].substring(1));
|
||||
}
|
||||
|
||||
this._pressedKey = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the current value of the input while preserving the caret position.
|
||||
*/
|
||||
_updateValue: function(str) {
|
||||
let start = this.input.selectionStart;
|
||||
this.input.value = str;
|
||||
this.input.setSelectionRange(start, start);
|
||||
this._updateSize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Split the provided string at the provided index. Returns an array of two strings.
|
||||
* _splitStringAt("1234567", 3) will return ["123", "4567"]
|
||||
*/
|
||||
_splitStringAt: function(str, index) {
|
||||
return [str.substring(0, index), str.substring(index, str.length)];
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the current input is displaying more than one line of text.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
|
|||
[browser_html_tooltip_xul-wrapper.js]
|
||||
[browser_inplace-editor-01.js]
|
||||
[browser_inplace-editor-02.js]
|
||||
[browser_inplace-editor_autoclose_parentheses.js]
|
||||
[browser_inplace-editor_autocomplete_01.js]
|
||||
[browser_inplace-editor_autocomplete_02.js]
|
||||
[browser_inplace-editor_autocomplete_offset.js]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* import-globals-from helper_inplace_editor.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
|
||||
const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
|
||||
loadHelperScript("helper_inplace_editor.js");
|
||||
|
||||
// Test the inplace-editor closes parentheses automatically.
|
||||
|
||||
// format :
|
||||
// [
|
||||
// what key to press,
|
||||
// expected input box value after keypress,
|
||||
// selected suggestion index (-1 if popup is hidden),
|
||||
// number of suggestions in the popup (0 if popup is hidden),
|
||||
// ]
|
||||
const testData = [
|
||||
["u", "u", -1, 0],
|
||||
["r", "ur", -1, 0],
|
||||
["l", "url", -1, 0],
|
||||
["(", "url()", -1, 0],
|
||||
["v", "url(v)", -1, 0],
|
||||
["a", "url(va)", -1, 0],
|
||||
["r", "url(var)", -1, 0],
|
||||
["(", "url(var())", -1, 0],
|
||||
["-", "url(var(-))", -1, 0],
|
||||
["-", "url(var(--))", -1, 0],
|
||||
["a", "url(var(--a))", -1, 0],
|
||||
[")", "url(var(--a))", -1, 0],
|
||||
[")", "url(var(--a))", -1, 0],
|
||||
];
|
||||
|
||||
add_task(async function() {
|
||||
await addTab("data:text/html;charset=utf-8," +
|
||||
"inplace editor parentheses autoclose");
|
||||
let [host, win, doc] = await createHost();
|
||||
|
||||
let xulDocument = win.top.document;
|
||||
let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
|
||||
await new Promise(resolve => {
|
||||
createInplaceEditorAndClick({
|
||||
start: runPropertyAutocompletionTest,
|
||||
contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
|
||||
property: {
|
||||
name: "background-image"
|
||||
},
|
||||
cssVariables: new Map(),
|
||||
done: resolve,
|
||||
popup: popup
|
||||
}, doc);
|
||||
});
|
||||
|
||||
popup.destroy();
|
||||
host.destroy();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
let runPropertyAutocompletionTest = async function(editor) {
|
||||
info("Starting to test for css property completion");
|
||||
|
||||
// No need to test autocompletion here, return an empty array.
|
||||
editor._getCSSValuesForPropertyName = () => [];
|
||||
|
||||
for (let data of testData) {
|
||||
await testCompletion(data, editor);
|
||||
}
|
||||
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
|
||||
};
|
||||
|
|
@ -27,16 +27,16 @@ const testData = [
|
|||
["v", "v", -1, 0, null],
|
||||
["a", "va", -1, 0, null],
|
||||
["r", "var", -1, 0, null],
|
||||
["(", "var(", -1, 0, null],
|
||||
["-", "var(--abc", 0, 4, "blue"],
|
||||
["VK_BACK_SPACE", "var(-", -1, 0, null],
|
||||
["-", "var(--abc", 0, 4, "blue"],
|
||||
["VK_DOWN", "var(--def", 1, 4, "red"],
|
||||
["VK_DOWN", "var(--ghi", 2, 4, "green"],
|
||||
["VK_DOWN", "var(--jkl", 3, 4, "yellow"],
|
||||
["VK_DOWN", "var(--abc", 0, 4, "blue"],
|
||||
["VK_DOWN", "var(--def", 1, 4, "red"],
|
||||
["VK_LEFT", "var(--def", -1, 0, null],
|
||||
["(", "var()", -1, 0, null],
|
||||
["-", "var(--abc)", 0, 4, "blue"],
|
||||
["VK_BACK_SPACE", "var(-)", -1, 0, null],
|
||||
["-", "var(--abc)", 0, 4, "blue"],
|
||||
["VK_DOWN", "var(--def)", 1, 4, "red"],
|
||||
["VK_DOWN", "var(--ghi)", 2, 4, "green"],
|
||||
["VK_DOWN", "var(--jkl)", 3, 4, "yellow"],
|
||||
["VK_DOWN", "var(--abc)", 0, 4, "blue"],
|
||||
["VK_DOWN", "var(--def)", 1, 4, "red"],
|
||||
["VK_LEFT", "var(--def)", -1, 0, null],
|
||||
];
|
||||
|
||||
const CSS_VARIABLES = [
|
||||
|
|
|
|||
Loading…
Reference in a new issue