Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed problems with colon character #363

Closed
wants to merge 12 commits into from

Conversation

FezVrasta
Copy link

This pull should fixes problems with the colon character when used on pseudo-classes and other new CSS3 cases.

Before this code:

a:hover {
    color:black;
}
::selection {
    color:black;
}

was converted in:

a: hover {
    color: black;
}
: : selection {
    color: black;
}

Notice the extra white-spaces added after each colon character.
My fix should have fixed this problem.

This pull should fixes problems with the colon character when used on pseudo-classes and other new CSS3 cases.
var test1 = source_text.substr(pos, source_text.indexOf(';') + 1);
var test2 = source_text.substr(pos, source_text.indexOf('{') + 1);

if(test1.length > test2.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please match the style of the file: 4 spaces per tab, space between if keyword and opening paren.

@bitwiseman
Copy link
Member

Great, please at least add tests for your fixes (and related inputs). If possible, please translate to python css beautifier as well.

@FezVrasta
Copy link
Author

I don't know how to add tests and I don't know python. I'll fix the formatting problems.

With my fix there is an edge case bug that I'm trying to fix on Stackoverflow

@bitwiseman
Copy link
Member

If you can't do the python, that is fine. I can do the translation.

However, we're not taking significant changes in this area without tests to demonstrate proper functioning and protect from future regressions. Here is an example of someone adding tests for css-beautify changes. It is easy and straight-forward.

8d27ea1

@FezVrasta
Copy link
Author

I must be stupid but I can't understand how the file you have linked me could act as test...

@bitwiseman
Copy link
Member

Add lines to that file (just like in the commit I linked) that follow the pattern:

btc("input_css", "expected_output_css");

btc() is a helper function that runs the input_css through the beautifier and then checks that the resulting text matches expected_output_css.

@FezVrasta
Copy link
Author

I hope it's ok how I did.

@evocateur
Copy link
Contributor

After writing the tests, you need to run them with npm test. (If you haven't already, run npm install . in the root of your clone before running the tests.)

As written, the tests are failing with this output:

---- css_beautify input -------
#foo : hover{color:purple}
---- css_beautify expected ----
#foo:hover {
  color: purple
}

---- css_beautify output ------
#foo: hover {
  color: purple
}


---- css_beautify input -------
#foo:hover {
  color: purple
}

---- css_beautify expected ----
#foo:hover {
  color: purple
}

---- css_beautify output ------
#foo: hover {
  color: purple
}


---- css_beautify input -------
: : selection {
color: #ff0000;}
---- css_beautify expected ----
::selection {
  color: #ff0000;
}
---- css_beautify output ------
::selection {
  color: #ff0000;
}


---- css_beautify input -------
::selection {
  color: #ff0000;
}
---- css_beautify expected ----
::selection {
  color: #ff0000;
}
---- css_beautify output ------
::selection {
  color: #ff0000;
}


4 tests failed.

You'll notice there are extra line breaks after all of the output, and that the pseudo-class selector is still mangled.

Also, there are still stylistic anomalies in beautify-css.js. You can run js-beautify on it in-place to resolve these discrepancies:

npm -g install js-beautify
js-beautify -jr js/lib/beautify-css.js

@FezVrasta
Copy link
Author

I've not git on my PC, don't know if I can use npm on Windows even.

By the way I've noticed that the problem is if the file starts with something that doesn't have colon in it... I'll try to fix this problem.


// colon character
btc("#foo : hover{color:purple}","#foo:hover {\n color: purple\n}\n");
btc(": : selection {\ncolor: #ff0000;}", "::selection {\n color: #ff0000;\n}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These inputs are somewhat nonsensical, as they don't represent valid CSS syntax. It would be clearer if you just asserted properly formatted input (and then omit the second parameter, because it defaults to the first).

btc("#foo:hover {\n  color: purple\n}\n");
btc("::selection {\n  color: #ff0000;\n}\n");

Note that the second test has an added \n, which fixes one breaking test. The #foo:hover case is still breaking.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that the first parameter was the not formatted input and the second one the formatted one. Isn't it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the input and output are expected to be identical, all the test methods support simply passing one argument.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My input and output was not identical.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like I said initially, they should be. Who in their right mind writes invalid CSS syntax and then expects it to magically work?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get what you mean, but if the beautifier can even remove extra spaces and make the syntax valid I can't see the problem. I didn't added extra code to do it, it's just a side effect of my patch.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, yes, I want to take a part in this bikeshed as well!

The tests by themselves are ok — we have weird corner cases and some broken-code-formatting checks in the jsbeautifier code as well. The strong uneasiness I myself feel is that the tests need to clearly cover the proper input first, but currently there are no tests that make sure that the beautifier formats a clean, proper css code as well.

I.e something like this would make the tests much more structured and clear:

// selector support
btc("#foo :hover {...}", "#foo :hover {...}");
...
// handle some garbage as well, just because we can
btc(" : : selection", "::selection")

...and then we might have a separate discussion WHY would we ever want to test the garbage cases and what do we achieve by that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Although, for the purposes of this bugfix, #foo :hover is not valid CSS syntax. Pseudo-classes must always be attached to other selectors (some pseudo-elements (doubled colons), however, can be distinct).

@FezVrasta
Copy link
Author

I've just written a different patch for this problem, I can't find bugs with this method. The tests should be the same.

@evocateur
Copy link
Contributor

And yet, things are still failing: https://travis-ci.org/einars/js-beautify/jobs/14276649

@@ -233,7 +233,19 @@
insideRule = false;
} else if (ch === ":") {
eatWhitespace();
output.push(ch, " ");

var text_after_pos = source_text.replace(new RegExp("('|\").*('|\")", "gm"), "").substr(pos - 1) + ";",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running a global replace with a non-reusable regexp on the source content every time a colon is encountered is dramatically bad practice.

@FezVrasta
Copy link
Author

This one is a problem of the test, the correct output is the one provided as css_beautify output. Notice the space between the colon and the not.

---- css_beautify input -------
a: not("foobar\";{}omg"){
content: 'example\';{} text';
content: "example\";{} text";}
---- css_beautify expected ----
a: not("foobar\";{}omg") {
    content: 'example';{} text';
    content: "example\";{} text";
}
---- css_beautify output ------
a:not("foobar\";{}omg") {
  content: 'example\';{} text';
  content: "example\";{} text";
}

The problem with the document starting with double dots is a problem that I will fix asap.
The other error is a typo of my test, there is a single quote between two single quotes, and this is a probem because makes the CSS not valid.

About bad practice etc:

honestly I think that keep the beautifier as-is, with lot of bugs and cases not supported is even worse than have some hack inside the code. But this is your choice. Personally I will use my patch for my projects.

There's not a function because would be useless to have a function that is called only one time, just because it's very specific as I've said before.

@FezVrasta
Copy link
Author

About the regex, maybe it could be performed only one time as first action of the beautifier. This would make everything faster and nicer.

@bitwiseman
Copy link
Member

Regex over the entire input once first? Um, no.

@evocateur
Copy link
Contributor

Interestingly, as long as one does not attempt to fix illegal syntax, I was able to accomplish this with far less confusing code:

diff --git a/js/lib/beautify-css.js b/js/lib/beautify-css.js
index 8830659..4e23dc6 100644
--- a/js/lib/beautify-css.js
+++ b/js/lib/beautify-css.js
@@ -204,6 +204,7 @@
         /*_____________________--------------------_____________________*/

         var insideRule = false;
+        var blockLevel = 0;
         while (true) {
             var isAfterSpace = skipWhitespace();

@@ -226,27 +227,29 @@
                 } else {
                     indent();
                     print["{"](ch);
+                    blockLevel += 1;
                 }
             } else if (ch === '}') {
                 outdent();
                 print["}"](ch);
                 insideRule = false;
+                blockLevel -= 1;
             } else if (ch === ":") {
                 eatWhitespace();
-
-                var text_after_pos = source_text.replace(new RegExp("('|\").*('|\")", "gm"), "").substr(pos - 1) + ";",
-                    semicolon = text_after_pos.substr(0, text_after_pos.indexOf(';')).length,
-                    closed_brace = text_after_pos.substr(0, text_after_pos.indexOf('}')).length,
-                    open_brace = text_after_pos.substr(0, text_after_pos.indexOf('{')).length,
-                    test1 = (semicolon > closed_brace) ? closed_brace : semicolon;
-
-                if (test1 > open_brace && open_brace !== 0) {
-                    output.push(ch);
-                } else {
+                if (blockLevel) {
+                    // 'property: value' delimiter
                     output.push(ch, " ");
+                    insideRule = true;
+                } else {
+                    if (peek() === ":") {
+                        // pseudo-element
+                        next();
+                        output.push("::");
+                    } else {
+                        // pseudo-class
+                        output.push(ch);
+                    }
                 }
-
-                insideRule = true;
             } else if (ch === '"' || ch === '\'') {
                 output.push(eatString(ch));
             } else if (ch === ';') {
diff --git a/js/test/beautify-tests.js b/js/test/beautify-tests.js
index 69c9678..6de0f0b 100755
--- a/js/test/beautify-tests.js
+++ b/js/test/beautify-tests.js
@@ -1760,14 +1760,21 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,

         btc("#bla, #foo{color:green}", "#bla, #foo {\n  color: green\n}\n");
         btc("@media print {.tab{}}", "@media print {\n  .tab {}\n}\n");
+        btc("@media print {.tab,.bat{}}", "@media print {\n  .tab, .bat {}\n}\n");
+        // btc("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n  .tab, .bat:hover {\n    color: red\n  }\n}\n");
         btc("#bla, #foo{color:black}", "#bla, #foo {\n  color: black\n}\n");
-        
+
         // colon character
-        btc("#foo : hover{color:purple}","#foo:hover {\n  color: purple\n}\n");
-        btc(": : selection {\ncolor: #ff0000;}", "::selection {\n  color: #ff0000;\n}");
-        
+        btc("#foo:hover {\n  color: purple\n}\n");
+        btc("#foo *:hover {\n  color: purple\n}\n");
+        btc("::selection {\n  color: #ff0000;\n}\n");
+
+        // btc("#foo : hover{color:purple}","#foo:hover {\n  color: purple\n}\n");
+        // btc(": : selection {\ncolor: #ff0000;}", "::selection {\n  color: #ff0000;\n}");
+
         // particular edge case with braces and semicolons inside tags that allows custom text
-        btc("a: not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}", "a: not(\"foobar\\\";{}omg\") {\n    content: 'example\\';{} text';\n    content: \"example\\\";{} text\";\n}");
+        btc("a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
+            "a:not(\"foobar\\\";{}omg\") {\n  content: 'example\\';{} text';\n  content: \"example\\\";{} text\";\n}\n");

         return sanitytest;
     }

With one huge caveat

If you uncomment the commented @media test I added, you'll find that nested blocks fail with my simplified code. Fairly certain they fail with the existing patch, as well.

@evocateur
Copy link
Contributor

@FezVrasta
Copy link
Author

With my patch it doesn't fail, but it adds "tabs" instead of spaces, I really don't know why.

---- css_beautify input -------
@media screen {.tab,.bat:hover {color:red}}
---- css_beautify expected ----
@media screen {
  .tab, .bat:hover {
    color: red
  }
}
---- css_beautify output ------
@media screen {
    .tab,
    .bat:hover {
        color: red
    }
}

@evocateur evocateur closed this in f416b0b Nov 23, 2013
@evocateur
Copy link
Contributor

Since your tests never passed, I fixed it with a much simpler algorithm that doesn't allow malformed syntax, but does support nesting blocks. Thanks for raising the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants