Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit 549be0a

Browse files
author
Marcel Gerber
committed
Show colored swatch in CSS Code Hints and SVG Code Hints
1 parent b2df944 commit 549be0a

7 files changed

Lines changed: 202 additions & 21 deletions

File tree

src/extensions/default/CSSCodeHints/main.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,20 @@ define(function (require, exports, module) {
175175
return true;
176176
};
177177

178-
/*
178+
/**
179179
* Returns a sorted and formatted list of hints with the query substring
180180
* highlighted.
181181
*
182182
* @param {Array.<Object>} hints - the list of hints to format
183183
* @param {string} query - querystring used for highlighting matched
184-
* poritions of each hint
184+
* portions of each hint
185185
* @return {Array.jQuery} sorted Array of jQuery DOM elements to insert
186186
*/
187187
function formatHints(hints, query) {
188+
var hasColorSwatch = hints.some(function (token) {
189+
return token.color;
190+
});
191+
188192
StringMatch.basicMatchSort(hints);
189193
return hints.map(function (token) {
190194
var $hintObj = $("<span>").addClass("brackets-css-hints");
@@ -204,6 +208,10 @@ define(function (require, exports, module) {
204208
$hintObj.text(token.value);
205209
}
206210

211+
if (hasColorSwatch) {
212+
$hintObj = ColorUtils.formatColorHint($hintObj, token.color);
213+
}
214+
207215
$hintObj.data("token", token);
208216

209217
return $hintObj;
@@ -286,13 +294,19 @@ define(function (require, exports, module) {
286294

287295
valueArray = valueArray.concat(namedFlows);
288296
} else if (type === "color") {
289-
valueArray = valueArray.concat(ColorUtils.COLOR_NAMES);
297+
valueArray = valueArray.concat(ColorUtils.COLOR_NAMES.map(function (color) {
298+
return { text: color, color: color };
299+
}));
290300
valueArray.push("transparent", "currentColor");
291301
}
292302

293-
result = $.map(valueArray, function (pvalue, pindex) {
294-
var result = StringMatch.stringMatch(pvalue, valueNeedle, stringMatcherOptions);
303+
result = $.map(valueArray, function (pvalue) {
304+
var result = StringMatch.stringMatch(pvalue.text || pvalue, valueNeedle, stringMatcherOptions);
295305
if (result) {
306+
if (pvalue.color) {
307+
result.color = pvalue.color;
308+
}
309+
296310
return result;
297311
}
298312
});

src/extensions/default/CSSCodeHints/unittest-files/regions.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,13 @@ div.layout > div {
9393
flow-from: content;
9494
flow-from: element;
9595
}
96+
97+
/* colors */
98+
.colorful {
99+
color: ;
100+
background-color: ;
101+
border-left-color: deep;
102+
border-color: rent;
103+
height: ;
104+
color: transparent;
105+
}

src/extensions/default/CSSCodeHints/unittests.js

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,12 @@ define(function (require, exports, module) {
8080
}
8181

8282
// Ask provider for hints at current cursor position; expect it to return some
83-
function expectHints(provider, implicitChar) {
83+
function expectHints(provider, implicitChar, returnWholeObj) {
8484
expect(provider.hasHints(testEditor, implicitChar)).toBe(true);
8585
var hintsObj = provider.getHints();
8686
expect(hintsObj).toBeTruthy();
87-
return extractHintList(hintsObj.hints); // return just the array of hints
87+
// return just the array of hints if returnWholeObj is falsy
88+
return returnWholeObj ? hintsObj : extractHintList(hintsObj.hints);
8889
}
8990

9091
// Ask provider for hints at current cursor position; expect it NOT to return any
@@ -664,6 +665,77 @@ define(function (require, exports, module) {
664665
});
665666
});
666667

668+
describe("Color names and swatches in a CSS file", function () {
669+
beforeEach(function () {
670+
setupTest(testContentCSS, "css");
671+
});
672+
673+
afterEach(function () {
674+
tearDownTest();
675+
});
676+
677+
it("should list color names for color", function () {
678+
testEditor.setCursorPos({ line: 98, ch: 11 }); // after color
679+
var hintList = expectHints(CSSCodeHints.cssPropHintProvider);
680+
verifyAttrHints(hintList, "aliceblue"); // first hint should be aliceblue
681+
});
682+
683+
it("should show color swatches for background-color", function () {
684+
testEditor.setCursorPos({ line: 99, ch: 22 }); // after background-color
685+
var hints = expectHints(CSSCodeHints.cssPropHintProvider, undefined, true).hints;
686+
expect(hints[0].text()).toBe("aliceblue"); // first hint should be aliceblue
687+
expect(hints[0].find(".color-swatch").length).toBe(1);
688+
expect(hints[0].find(".color-swatch").css("backgroundColor")).toBe("rgb(240, 248, 255)");
689+
});
690+
691+
it("should filter out color names appropriately", function () {
692+
testEditor.setCursorPos({ line: 100, ch: 27 }); // after border-left-color
693+
var hintList = expectHints(CSSCodeHints.cssPropHintProvider);
694+
verifyAttrHints(hintList, "deeppink"); // first hint should be deeppink
695+
verifyAllValues(hintList, ["deeppink", "deepskyblue"]);
696+
});
697+
698+
it("should always include transparent and currentColor and they should not have a swatch, but class no-swatch-margin", function () {
699+
testEditor.setCursorPos({ line: 101, ch: 22 }); // after border-color
700+
var hints = expectHints(CSSCodeHints.cssPropHintProvider, undefined, true).hints,
701+
hintList = extractHintList(hints);
702+
verifyAttrHints(hintList, "currentColor"); // first hint should be currentColor
703+
verifyAllValues(hintList, ["currentColor", "darkmagenta", "transparent"]);
704+
expect(hints[0].find(".color-swatch").length).toBe(0); // no swatch for currentColor
705+
expect(hints[2].find(".color-swatch").length).toBe(0); // no swatch for transparent
706+
expect(hints[0].hasClass("no-swatch-margin")).toBeTruthy(); // no-swatch-margin applied to currentColor
707+
expect(hints[2].hasClass("no-swatch-margin")).toBeTruthy(); // no-swatch-margin applied to transparent
708+
});
709+
710+
it("should remove class no-swatch-margin from transparent if it's the only one in the list", function () {
711+
testEditor.setCursorPos({ line: 103, ch: 22 }); // after color
712+
var hints = expectHints(CSSCodeHints.cssPropHintProvider, undefined, true).hints,
713+
hintList = extractHintList(hints);
714+
verifyAllValues(hintList, ["transparent"]);
715+
expect(hints[0].find(".color-swatch").length).toBe(0); // no swatch for transparent
716+
expect(hints[0].hasClass("no-swatch-margin")).toBeFalsy(); // no-swatch-margin not applied to transparent
717+
});
718+
719+
it("should insert color names correctly", function () {
720+
var expectedString = " border-left-color: deeppink;",
721+
line = 100;
722+
723+
testEditor.setCursorPos({ line: line, ch: 27 }); // after border-left-color
724+
expectHints(CSSCodeHints.cssPropHintProvider);
725+
selectHint(CSSCodeHints.cssPropHintProvider, "deeppink");
726+
expect(testDocument.getLine(line).length).toBe(expectedString.length);
727+
expect(testDocument.getLine(line)).toBe(expectedString);
728+
expectCursorAt({ line: line, ch: expectedString.length - 1 });
729+
});
730+
731+
732+
it("should not display color names for unrelated properties", function () {
733+
testEditor.setCursorPos({ line: 102, ch: 12 }); // after height
734+
var hintList = expectHints(CSSCodeHints.cssPropHintProvider);
735+
expect(hintList.indexOf("aliceblue")).toBe(-1);
736+
});
737+
});
738+
667739
describe("Should not invoke CSS hints on space key", function () {
668740
beforeEach(function () {
669741
setupTest(testContentHTML, "html");

src/extensions/default/SVGCodeHints/main.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ define(function (require, exports, module) {
102102
* @return {Array.jQuery} sorted Array of jQuery DOM elements to insert
103103
*/
104104
function formatHints(hints, query) {
105+
var hasColorSwatch = hints.some(function (token) {
106+
return token.color;
107+
});
108+
105109
StringMatch.basicMatchSort(hints);
106110
return hints.map(function (token) {
107111
var $hintObj = $("<span>").addClass("brackets-svg-hints");
@@ -121,6 +125,10 @@ define(function (require, exports, module) {
121125
$hintObj.text(token.value);
122126
}
123127

128+
if (hasColorSwatch) {
129+
$hintObj = ColorUtils.formatColorHint($hintObj, token.color);
130+
}
131+
124132
$hintObj.data("token", token);
125133

126134
return $hintObj;
@@ -210,7 +218,9 @@ define(function (require, exports, module) {
210218
isMultiple = attributeData[tagInfo.attrName].multiple;
211219

212220
if (attributeData[tagInfo.attrName].type === "color") {
213-
options = ColorUtils.COLOR_NAMES;
221+
options = ColorUtils.COLOR_NAMES.map(function (color) {
222+
return { text: color, color: color };
223+
});
214224
options = options.concat(["currentColor", "transparent"]);
215225
}
216226
}
@@ -223,8 +233,12 @@ define(function (require, exports, module) {
223233
query = XMLUtils.getValueQuery(tagInfo);
224234
hints = $.map(options, function (option) {
225235
if (tagInfo.exclusionList.indexOf(option) === -1) {
226-
var match = StringMatch.stringMatch(option, query, stringMatcherOptions);
236+
var match = StringMatch.stringMatch(option.text || option, query, stringMatcherOptions);
227237
if (match) {
238+
if (option.color) {
239+
match.color = option.color;
240+
}
241+
228242
return match;
229243
}
230244
}

src/extensions/default/SVGCodeHints/unittests.js

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ define(function (require, exports, module) {
3939
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" +
4040
" width=\"200\" height=\"200\" preserveAspectRatio=\"xMinYMin meet\">\n" +
4141
" <title>Brackets SVG Code Hints</title>\n" +
42-
" <rect width=\"200\" height=\"200\" baseline-shift=\"baseline\" alignment-baseline=\"alphabetic\" stroke-width=\"1\"></rect>\n" +
43-
" <rect width='160' height='160' x='20' y='20' baseline-shift='super' alignment-baseline='baseline' />\n" +
42+
" <rect width=\"200\" height=\"200\" baseline-shift=\"baseline\" alignment-baseline=\"alphabetic\" stroke-width=\"1\" color=\"\"></rect>\n" +
43+
" <rect width='160' height='160' x='20' y='20' baseline-shift='super' alignment-baseline='baseline' color='rent' fill='transparent' />\n" +
4444
" <g>\n" +
4545
" \n" +
4646
" </g>\n" +
@@ -205,7 +205,7 @@ define(function (require, exports, module) {
205205
expectNoHints(SVGCodeHints.hintProvider);
206206

207207
// Between <rect></rect>
208-
testEditor.setCursorPos({line: 4, ch: 110});
208+
testEditor.setCursorPos({line: 4, ch: 119});
209209
expectNoHints(SVGCodeHints.hintProvider);
210210

211211
// After <title>+space in <title> Brackets
@@ -216,21 +216,21 @@ define(function (require, exports, module) {
216216

217217
it("should not hint after the closed tag", function () {
218218
// After </rect>
219-
testEditor.setCursorPos({line: 4, ch: 117});
219+
testEditor.setCursorPos({line: 4, ch: 126});
220220
expectNoHints(SVGCodeHints.hintProvider);
221221

222222
// After space at </rect>+space
223-
testDocument.replaceRange(" ", {line: 4, ch: 117});
224-
testEditor.setCursorPos({line: 4, ch: 118});
223+
testDocument.replaceRange(" ", {line: 4, ch: 126});
224+
testEditor.setCursorPos({line: 4, ch: 127});
225225
expectNoHints(SVGCodeHints.hintProvider);
226226

227227
// After />
228-
testEditor.setCursorPos({line: 5, ch: 104});
228+
testEditor.setCursorPos({line: 5, ch: 136});
229229
expectNoHints(SVGCodeHints.hintProvider);
230230

231231
// After space in />+space
232-
testDocument.replaceRange(" ", {line: 5, ch: 104});
233-
testEditor.setCursorPos({line: 5, ch: 105});
232+
testDocument.replaceRange(" ", {line: 5, ch: 136});
233+
testEditor.setCursorPos({line: 5, ch: 137});
234234
expectNoHints(SVGCodeHints.hintProvider);
235235

236236
// After </g>
@@ -453,6 +453,38 @@ define(function (require, exports, module) {
453453
});
454454
});
455455

456+
describe("Color names and swatches", function () {
457+
it("should show color swatches", function () {
458+
// After color="
459+
testEditor.setCursorPos({line: 4, ch: 117});
460+
var hints = expectHints(SVGCodeHints.hintProvider);
461+
verifyHints(hints, "aliceblue"); // first hint should be aliceblue
462+
expect(hints[0].find(".color-swatch").length).toBe(1);
463+
expect(hints[0].find(".color-swatch").css("backgroundColor")).toBe("rgb(240, 248, 255)");
464+
});
465+
466+
it("should always include transparent and currentColor and they should not have a swatch, but class no-swatch-margin", function () {
467+
// After color='rent
468+
testEditor.setCursorPos({line: 5, ch: 113});
469+
var hints = expectHints(SVGCodeHints.hintProvider);
470+
verifyHints(hints, "currentColor"); // first hint should be currentColor
471+
expect(hints[0].find(".color-swatch").length).toBe(0); // no swatch for currentColor
472+
expect(hints[2].find(".color-swatch").length).toBe(0); // no swatch for transparent
473+
expect(hints[0].hasClass("no-swatch-margin")).toBeTruthy(); // no-swatch-margin applied to currentColor
474+
expect(hints[2].hasClass("no-swatch-margin")).toBeTruthy(); // no-swatch-margin applied to transparent
475+
});
476+
477+
it("should remove class no-swatch-margin from transparent if it's the only one in the list", function () {
478+
// After fill='transparent
479+
testEditor.setCursorPos({line: 5, ch: 132});
480+
var hints = expectHints(SVGCodeHints.hintProvider);
481+
verifyHints(hints, "transparent");
482+
expect(hints.length).toBe(1); // transparent should be the only hint
483+
expect(hints[0].find(".color-swatch").length).toBe(0); // no swatch for transparent
484+
expect(hints[0].hasClass("no-swatch-margin")).toBeFalsy(); // no-swatch-margin not applied to transparent
485+
});
486+
});
487+
456488
describe("Tag Insertion", function () {
457489
it("should insert if query is empty", function () {
458490
// After < inside <g>

src/styles/brackets_patterns_override.less

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,25 @@ a:focus {
526526
background-color: @dark-bc-bg-highlight;
527527
}
528528
}
529+
530+
.color-swatch {
531+
border-radius: 2px;
532+
width: 12px;
533+
height: 12px;
534+
float: left;
535+
margin: 2px -8px 0 4px;
536+
box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.24);
537+
538+
.dark & {
539+
box-shadow: inset 0 0 1px rgba(255, 255, 255, 0.24);
540+
}
541+
}
542+
543+
// Used to align non-swatch items with the others. Only applied
544+
// if there's at least one item with a swatch in the list
545+
.no-swatch-margin {
546+
margin-left: 8px;
547+
}
529548
}
530549
}
531550
}

src/utils/ColorUtils.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
*/
2323

2424
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
25-
/*global define */
25+
/*global define, $ */
2626

2727
/**
2828
* Utilities functions related to color matching
@@ -48,7 +48,27 @@ define(function (require, exports, module) {
4848
// use RegExp.source of the RegExp literal to avoid doubled backslashes
4949
var COLOR_REGEX = new RegExp(/#[a-f0-9]{6}\b|#[a-f0-9]{3}\b|\brgb\(\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*\)|\brgb\(\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:[0-9]{1,2}%|100%)\s*\)|\brgba\(\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:1|1\.0|0|0?\.[0-9]{1,3})\s*\)|\brgba\(\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:1|1\.0|0|0?\.[0-9]{1,3})\s*\)|\bhsl\(\s*(?:[0-9]{1,3})\b\s*,\s*(?:[0-9]{1,2}|100)\b%\s*,\s*(?:[0-9]{1,2}|100)\b%\s*\)|\bhsla\(\s*(?:[0-9]{1,3})\b\s*,\s*(?:[0-9]{1,2}|100)\b%\s*,\s*(?:[0-9]{1,2}|100)\b%\s*,\s*(?:1|1\.0|0|0?\.[0-9]{1,3})\s*\)|\b/.source + COLOR_NAMES.join("\\b|\\b") + "\\b", "gi");
5050

51+
/*
52+
* Adds a color swatch to code hints where this is supported.
53+
* @param {!jQuery} $hintObj - list item where the swatch will be in
54+
* @param {?string} color - color the swatch should have, or null to add extra left margin to
55+
* align with the other hints
56+
* @return {jQuery} jQuery object with the correct class and/or style applied
57+
*/
58+
function formatColorHint($hintObj, color) {
59+
if (color) {
60+
$hintObj.prepend($("<span>")
61+
.addClass("color-swatch")
62+
.css("backgroundColor", color));
63+
} else {
64+
$hintObj.addClass("no-swatch-margin");
65+
}
66+
return $hintObj;
67+
}
68+
69+
5170
// Define public API
52-
exports.COLOR_NAMES = COLOR_NAMES;
53-
exports.COLOR_REGEX = COLOR_REGEX;
71+
exports.COLOR_NAMES = COLOR_NAMES;
72+
exports.COLOR_REGEX = COLOR_REGEX;
73+
exports.formatColorHint = formatColorHint;
5474
});

0 commit comments

Comments
 (0)