Bug 1422635: Implement CSS variable autocompletion. r=jdescottes draft
authorRajdeep Nanua <rajdeep.nanua@mail.utoronto.ca>
Tue, 12 Dec 2017 01:47:59 -0500
changeset 720357 392f755e7138a6cada9bdbe106c97523f29980d4
parent 719034 1ce9b4193d624d5d4ed4bceecfbeb1514f818848
child 720358 9a4ab6f462a8249df34b1718979085ec507e1275
child 720361 278d7876d41b717a799c91e341d582f469e505b4
push id95525
push userjdescottes@mozilla.com
push dateMon, 15 Jan 2018 11:12:38 +0000
reviewersjdescottes
bugs1422635
milestone59.0a1
Bug 1422635: Implement CSS variable autocompletion. r=jdescottes Initial support for CSS variable autocompletion in ruleview. MozReview-Commit-ID: AlblDmyW4Iq
devtools/client/inspector/rules/views/text-property-editor.js
devtools/client/shared/inplace-editor.js
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -311,16 +311,17 @@ TextPropertyEditor.prototype = {
         advanceChars: advanceValidate,
         contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
         property: this.prop,
         defaultIncrement: this.prop.name === "opacity" ? 0.1 : 1,
         popup: this.popup,
         multiline: true,
         maxWidth: () => this.container.getBoundingClientRect().width,
         cssProperties: this.cssProperties,
+        cssVariables: this.rule.elementStyle.variables,
       });
 
       this.ruleView.highlighters.on("hover-shape-point", this._onHoverShapePoint);
     }
   },
 
   /**
    * Get the path from which to resolve requests for this
--- a/devtools/client/shared/inplace-editor.js
+++ b/devtools/client/shared/inplace-editor.js
@@ -117,16 +117,17 @@ function isKeyIn(key, ...keys) {
  *       available if multiline is true. If a function is provided, it will be
  *       called when replacing the element by the inplace input.
  *    {Boolean} trimOutput: Should the returned string be trimmed?
  *      defaults to true
  *    {Boolean} preserveTextStyles: If true, do not copy text-related styles
  *              from `element` to the new input.
  *      defaults to false
  *    {Object} cssProperties: An instance of CSSProperties.
+ *    {Object} cssVariables: A Map object containing all CSS variables.
  *    {Number} defaultIncrement: The value by which the input is incremented
  *      or decremented by default (0.1 for properties like opacity and 1 by default)
  */
 function editableField(options) {
   return editableItem(options, function (element, event) {
     if (!options.element.inplaceEditor) {
       new InplaceEditor(options, event);
     }
@@ -219,16 +220,17 @@ function getInplaceEditorForSpan(span) {
 exports.getInplaceEditorForSpan = getInplaceEditorForSpan;
 
 function InplaceEditor(options, event) {
   this.elt = options.element;
   let doc = this.elt.ownerDocument;
   this.doc = doc;
   this.elt.inplaceEditor = this;
   this.cssProperties = options.cssProperties;
+  this.cssVariables = options.cssVariables || new Map();
   this.change = options.change;
   this.done = options.done;
   this.contextMenu = options.contextMenu;
   this.defaultIncrement = options.defaultIncrement || 1;
   this.destroy = options.destroy;
   this.initial = options.initial ? options.initial : this.elt.textContent;
   this.multiline = options.multiline || false;
   this.maxWidth = options.maxWidth;
@@ -1328,18 +1330,26 @@ InplaceEditor.prototype = {
         // Get the last query to be completed before the caret.
         let match = /([^\s,.\/]+$)/.exec(query);
         if (match) {
           startCheckQuery = match[0];
         } else {
           startCheckQuery = "";
         }
 
-        list = ["!important",
-                ...this._getCSSValuesForPropertyName(this.property.name)];
+        // Check if the query to be completed is a CSS variable.
+        let varMatch = /^var\(([^\s]+$)/.exec(startCheckQuery);
+
+        if (varMatch && varMatch.length == 2) {
+          startCheckQuery = varMatch[1];
+          list = this._getCSSVariableNames();
+        } else {
+          list = ["!important",
+                  ...this._getCSSValuesForPropertyName(this.property.name)];
+        }
 
         if (query == "") {
           // Do not suggest '!important' without any manually typed character.
           list.splice(0, 1);
         }
       } else if (this.contentType == CONTENT_TYPES.CSS_MIXED &&
                  /^\s*style\s*=/.test(query)) {
         // Check if the style attribute is closed before the selection.
@@ -1485,16 +1495,25 @@ InplaceEditor.prototype = {
    * mocked suggestion lists.
    *
    * @param {String} propertyName
    * @return {Array} array of CSS property values (Strings)
    */
   _getCSSValuesForPropertyName: function (propertyName) {
     return this.cssProperties.getValues(propertyName);
   },
+
+  /**
+   * Returns the list of all CSS variables to use for the autocompletion.
+   *
+   * @return {Array} array of CSS variable names (Strings)
+   */
+  _getCSSVariableNames: function () {
+    return Array.from(this.cssVariables.keys()).sort();
+  },
 };
 
 /**
  * Copy text-related styles from one element to another.
  */
 function copyTextStyles(from, to) {
   let win = from.ownerDocument.defaultView;
   let style = win.getComputedStyle(from);