Skip to content

Commit 090cc57

Browse files
committed
fix: color preview for jsp, php, scss and other varients working and tests
1 parent a87ad51 commit 090cc57

File tree

5 files changed

+143
-83
lines changed

5 files changed

+143
-83
lines changed

src/extensions/default/DebugCommands/MacroRunner.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,9 +361,10 @@ define(function (require, exports, module) {
361361
}
362362
}
363363

364-
function validateEqual(obj1, obj2) {
364+
function validateEqual(obj1, obj2, message = "") {
365365
if(!_.isEqual(obj1, obj2)){
366-
throw new Error(`validateEqual: expected ${JSON.stringify(obj1)} to equal ${JSON.stringify(obj2)}`);
366+
throw new Error(`validateEqual: ${ message ? message + "\n" : ""
367+
} expected ${JSON.stringify(obj1)} to equal ${JSON.stringify(obj2)}`);
367368
}
368369
}
369370

src/extensionsIntegrated/CSSColorPreview/main.js

Lines changed: 107 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -117,73 +117,74 @@ define(function (require, exports, module) {
117117
/**
118118
* To display the color marks on the gutter
119119
*
120-
* @param {activeEditor} editor
120+
* @param {Editor} editor
121121
* @param {Array.<object>} _results An array of objects which stores
122122
* all the line numbers and the colors to be displayed on that line.
123123
* @param {Boolean} update marks whether this function is called when some lines
124124
* are updated or when the whole file is re-updated. Defaults to false.
125125
*/
126126
function showGutters(editor, _results, update = false) {
127127
if (editor && enabled) {
128-
// if the file is updated we don't need to clear the gutter
129-
// as it will clear all the existing markers.
130-
if(!update) {
131-
editor.clearGutter(GUTTER_NAME); // clear color markers
132-
}
133-
_addDummyGutterMarkerIfNotExist(editor, editor.getCursorPos().line);
134-
135-
// Only add markers if enabled
136-
if (enabled) {
137-
const colorGutters = _.sortBy(_results, "lineNumber");
138-
139-
colorGutters.forEach(function (obj) {
140-
let $marker;
141-
if (obj.colorValues.length === 1) {
142-
// Single color preview
143-
$marker = $("<i>")
144-
.addClass(SINGLE_COLOR_PREVIEW_CLASS)
145-
.css('background-color', obj.colorValues[0]);
146-
147-
editor.setGutterMarker(obj.lineNumber, GUTTER_NAME, $marker[0]);
148-
$marker.click((event)=>{
149-
event.preventDefault();
150-
event.stopPropagation();
151-
_colorIconClicked(editor, obj.lineNumber, obj.colorValues[0]);
152-
});
153-
} else {
154-
// Multiple colors preview
155-
$marker = $("<div>").addClass(MULTI_COLOR_PREVIEW_CLASS);
156-
157-
// Positions for up to 4 colors in grid
158-
const positions = [
159-
{ top: 0, left: 0 },
160-
{ top: 0, right: 0 },
161-
{ bottom: 0, right: 0 },
162-
{ bottom: 0, left: 0 }
163-
];
164-
165-
obj.colorValues.forEach((color, index) => {
166-
if (index < 4) {
167-
const $colorBox = $("<div>")
168-
.addClass("color-box")
169-
.css({
170-
'background-color': color,
171-
...positions[index]
128+
editor.operation(()=>{
129+
// if the file is updated we don't need to clear the gutter
130+
// as it will clear all the existing markers.
131+
if(!update) {
132+
editor.clearGutter(GUTTER_NAME); // clear color markers
133+
}
134+
_addDummyGutterMarkerIfNotExist(editor, editor.getCursorPos().line);
135+
136+
// Only add markers if enabled
137+
if (enabled) {
138+
const colorGutters = _.sortBy(_results, "lineNumber");
139+
140+
colorGutters.forEach(function (obj) {
141+
let $marker;
142+
if (obj.colorValues.length === 1) {
143+
// Single color preview
144+
$marker = $("<i>")
145+
.addClass(SINGLE_COLOR_PREVIEW_CLASS)
146+
.css('background-color', obj.colorValues[0]);
147+
148+
editor.setGutterMarker(obj.lineNumber, GUTTER_NAME, $marker[0]);
149+
$marker.click((event)=>{
150+
event.preventDefault();
151+
event.stopPropagation();
152+
_colorIconClicked(editor, obj.lineNumber, obj.colorValues[0]);
153+
});
154+
} else {
155+
// Multiple colors preview
156+
$marker = $("<div>").addClass(MULTI_COLOR_PREVIEW_CLASS);
157+
158+
// Positions for up to 4 colors in grid
159+
const positions = [
160+
{ top: 0, left: 0 },
161+
{ top: 0, right: 0 },
162+
{ bottom: 0, right: 0 },
163+
{ bottom: 0, left: 0 }
164+
];
165+
166+
obj.colorValues.forEach((color, index) => {
167+
if (index < 4) {
168+
const $colorBox = $("<div>")
169+
.addClass("color-box")
170+
.css({
171+
'background-color': color,
172+
...positions[index]
173+
});
174+
$colorBox.click((event)=>{
175+
event.preventDefault();
176+
event.stopPropagation();
177+
_colorIconClicked(editor, obj.lineNumber, color);
172178
});
173-
$colorBox.click((event)=>{
174-
event.preventDefault();
175-
event.stopPropagation();
176-
_colorIconClicked(editor, obj.lineNumber, color);
177-
});
178-
$marker.append($colorBox);
179-
}
180-
});
181-
182-
editor.setGutterMarker(obj.lineNumber, GUTTER_NAME, $marker[0]);
183-
}
184-
});
185-
}
179+
$marker.append($colorBox);
180+
}
181+
});
186182

183+
editor.setGutterMarker(obj.lineNumber, GUTTER_NAME, $marker[0]);
184+
}
185+
});
186+
}
187+
});
187188
}
188189
}
189190

@@ -249,6 +250,35 @@ define(function (require, exports, module) {
249250
}
250251
}
251252

253+
const STYLE_PARSE_LANGUAGES = {
254+
php: true,
255+
jsx: true,
256+
tsx: true
257+
};
258+
function _isInStyleAttr(editor, token) {
259+
while(token.type === "string") {
260+
const currentToken = token;
261+
token = editor.getPreviousToken({line: token.line, ch: token.start}, true);
262+
if(currentToken.line === token.line &&
263+
currentToken.start === token.start && currentToken.end === token.end) {
264+
// reached start of file
265+
break;
266+
}
267+
}
268+
return token.type === "attribute" && token.string === "style";
269+
}
270+
271+
function _shouldProcessToken(editor, token, pos) {
272+
const languageID = editor.document.getLanguage().getId();
273+
if(languageID === "html") {
274+
return editor.getLanguageForPosition(pos).getId() === "css";
275+
} else if (STYLE_PARSE_LANGUAGES[languageID]) {
276+
// unfortunately the codemirror mode doesn't support css detection in attributes in php files right now
277+
return token.type !== "comment" && _isInStyleAttr(editor, token);
278+
}
279+
return token.type !== "comment";
280+
}
281+
252282
/**
253283
* Detects valid colors in a given line of text
254284
*
@@ -264,7 +294,7 @@ define(function (require, exports, module) {
264294
return [];
265295
}
266296

267-
const valueRegex = /:[^;]*;/g;
297+
const valueRegex = /:[^;]*;?/g; // the last semi colon is optional.
268298
const validColors = [];
269299

270300
// Find all property value sections in the line
@@ -281,7 +311,7 @@ define(function (require, exports, module) {
281311
const token = editor.getToken({ line: lineNumber, ch: colorIndex }, true);
282312

283313
// If the token is not a comment, add the color
284-
if (token.type !== "comment") {
314+
if (_shouldProcessToken(editor, token, { line: lineNumber, ch: colorIndex })) {
285315
validColors.push({
286316
color: colorMatch[0],
287317
index: colorIndex
@@ -356,27 +386,33 @@ define(function (require, exports, module) {
356386
/**
357387
* Function that gets triggered when any change occurs on the editor
358388
*
359-
* @param {Editor} instance the codemirror instance
360-
* @param {Object} changeObj an object that has properties regarding the line changed and type of change
389+
* @param _evt unused event detail
390+
* @param {Editor} instance the editor instance
391+
* @param {Object} changeList an object that has properties regarding the line changed and type of change
361392
*/
362-
function onChanged(instance, changeObj) {
363-
364-
const editor = EditorManager.getActiveEditor();
365-
393+
function onChanged(_evt, instance, changeList) {
366394
// for insertion and deletion, update the changed lines
367-
if(changeObj.origin === '+input' || changeObj.origin === '+delete') {
368-
// make sure that the required properties exist and in the form they are expected to be
395+
if(!changeList || !changeList.length) {
396+
return;
397+
}
398+
const changeObj = changeList[0];
399+
if(changeList.length === 1 && changeObj.origin === '+input' || changeObj.origin === '+delete') {
400+
// we only do the diff updates on single key type input/delete and not bulk changes
401+
// somehow the performance degrades if we do the diff logic on large blocks.
369402
if(changeObj.from.line && changeObj.to.line && changeObj.from.line <= changeObj.to.line) {
370-
const aColors = updateColorMarks(editor, changeObj.from.line, changeObj.to.line);
371-
showGutters(editor, aColors, true);
403+
let toLine = changeObj.to.line;
404+
if(changeObj.text && changeObj.text.length) {
405+
toLine = changeObj.from.line + changeObj.text.length;
406+
}
407+
const aColors = updateColorMarks(instance, changeObj.from.line, Math.max(changeObj.to.line, toLine));
408+
showGutters(instance, aColors, true);
372409
} else {
373410
showColorMarks();
374411
}
375412

376413
} else { // for any complex operation like, cut, paste etc, we re-update the whole file
377414
showColorMarks();
378415
}
379-
380416
}
381417

382418
// init after appReady
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
.class-one {
3+
width: 100px;
4+
}
5+
6+
7+
.class-two {/*color:red;*/}
8+
.color-one {
9+
color: blue /*color:red;*/
10+
}
11+
12+
.class-3 {color: #00ff8c;}
13+
.class-4 {color: #00ff8c red;}
14+
.class-5 {color: #b7ff00 green #3e4395;}
15+
.class-6 {color: #ff0090 #802095 #954e3e #454e3e;}
16+
.class-6 {color: #ff0090 #802095 #954e3e #454e3e #150e3e;}

test/spec/CSSColorPreview-test-files/base.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
</head>
88
<body>
99
<h1 style="color: blue;">test color</h1>
10-
<h1>test no color</h1>
10+
<h1>test no color: green;</h1> <!--<h1 style="color: yellow;">test color</h1>-->
1111
<h1>test add color</h1>
12-
<div style="color: #00ff8c;">green color</div>
12+
<div style="color: #00ff8c">green color</div>
1313
<div style="color: #00ff8c red;">multiple colors 2</div>
1414
<div style="color: #b7ff00 green #3e4395;">multiple colors 3</div>
1515
<div style="color: #ff0090 #802095 #954e3e #454e3e;">multiple colors 4</div>

test/spec/Extn-CSSColorPreview-integ-test.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ define(function (require, exports, module) {
9595
}
9696
}
9797

98-
function testHTMLFile(fileName) {
98+
function testFile(baseFileName, fileName) {
9999
it(`should color gutter appear as expected ${fileName}`, async function () {
100-
const htmlText = await __PR.readTextFile("base.html");
100+
const htmlText = await __PR.readTextFile(baseFileName);
101101
await __PR.writeTextFile(fileName, htmlText, true);
102102
await __PR.openFile(fileName);
103103
const editor = EditorManager.getActiveEditor();
@@ -116,19 +116,22 @@ define(function (require, exports, module) {
116116
gutterMarker = editor.getGutterMarker(line, GUTTER_NAME);
117117
if (!colorBoxesInLines.includes(line)) {
118118
// there should be no color box here
119-
__PR.validateEqual(!!gutterMarker, false);
119+
__PR.validateEqual(!!gutterMarker, false,
120+
`expected no color box at line ${line}` + editor.getLine(line));
120121
} else if(singleColorBoxesInLines.includes(line)) {
121-
__PR.validateEqual(gutterMarker.classList.contains(SINGLE_COLOR_PREVIEW_CLASS), true);
122+
__PR.validateEqual(gutterMarker.classList.contains(SINGLE_COLOR_PREVIEW_CLASS), true,
123+
`expected single color box at line ${line}` + editor.getLine(line));
122124
} else if(multiColorBoxesInLines.includes(line)) {
123-
__PR.validateEqual(gutterMarker.classList.contains(MULTI_COLOR_PREVIEW_CLASS), true);
125+
__PR.validateEqual(gutterMarker.classList.contains(MULTI_COLOR_PREVIEW_CLASS), true,
126+
`expected multi color box at line ${line}` + editor.getLine(line));
124127
}
125128
}
126129
await __PR.closeFile();
127130

128131
});
129132

130133
it(`should color gutter show correct colors in box ${fileName}`, async function () {
131-
const htmlText = await __PR.readTextFile("base.html");
134+
const htmlText = await __PR.readTextFile(baseFileName);
132135
await __PR.writeTextFile(fileName, htmlText, true);
133136
await __PR.openFile(fileName);
134137
const editor = EditorManager.getActiveEditor();
@@ -152,9 +155,13 @@ define(function (require, exports, module) {
152155
// todo test beautify, block comment, line comment, comment at end of file, code changes, toggle color edit
153156
}
154157

155-
const htmlFiles = ["a.html", "a.htm", "a.xhtml", "a.php", "a.jsp"];
158+
const htmlFiles = ["a.html", "a.htm", "a.xhtml", "a.php", "a.jsp", "a.jsx", "a.tsx"];
156159
for (let htmlFile of htmlFiles){
157-
testHTMLFile(htmlFile);
160+
testFile("base.html", htmlFile);
161+
}
162+
const cssFiles = ["a.css", "a.less", "a.scss", "a.sass"];
163+
for (let cssFile of cssFiles){
164+
testFile("base.css", cssFile);
158165
}
159166
});
160167
});

0 commit comments

Comments
 (0)