Simplify CSS linter and clean up CSS.

The CSS linter was pretty hard to reason about.  It was
pretty flexible about certain things, but then it would
prevent seemingly innocuous code from getting checked in.

This commit overhauls the pretty-printer to be more composable,
where every object in the AST knows how to render itself.  It
also cleans up a little bit of the pre_fluff/post_fluff logic
in the parser itself, so comments are more likely to be "attached"
to the AST node that make sense.

The linter is actually a bit more finicky about newlines, but
this is mostly a good thing, as most of the variations before
this commit were pretty arbitrary.
This commit is contained in:
Steve Howell 2017-11-13 08:26:11 -08:00 committed by Tim Abbott
parent 7e2b452f6a
commit ba51078418
15 changed files with 147 additions and 220 deletions

View File

@ -160,7 +160,6 @@
background-color: hsl(33, 48%, 96%);
}
.new-style .button[disabled="disabled"] {
cursor: not-allowed;
-moz-filter: saturate(0);

View File

@ -125,7 +125,6 @@
box-shadow: none !important;
}
table.compose_table {
table-layout: fixed;
margin-left: auto;
@ -316,7 +315,6 @@ textarea.new_message_textarea:focus,
border-left: none;
}
input.recipient_box {
margin: 0px;
height: 1.1em;

View File

@ -43,7 +43,6 @@
}
}
/* popover */
.hotspot.overlay {
z-index: 104;
@ -124,6 +123,7 @@
.hotspot-popover:after {
border-width: 12px;
}
.hotspot-popover:before {
border-width: 13px;
}
@ -133,10 +133,12 @@
bottom: 100%;
right: 50%;
}
.hotspot-popover.arrow-top:after {
border-bottom-color: hsl(164, 44%, 47%);
margin-right: -12px;
}
.hotspot-popover.arrow-top:before {
border-bottom-color: hsl(0, 0%, 80%);
margin-right: -13px;
@ -147,10 +149,12 @@
right: 100%;
top: 50%;
}
.hotspot-popover.arrow-left:after {
border-right-color: #fff;
margin-top: -12px;
}
.hotspot-popover.arrow-left:before {
border-right-color: hsl(0, 0%, 80%);
margin-top: -13px;
@ -161,10 +165,12 @@
top: 100%;
right: 50%;
}
.hotspot-popover.arrow-bottom:after {
border-top-color: #fff;
margin-right: -12px;
}
.hotspot-popover.arrow-bottom:before {
border-top-color: hsl(0, 0%, 80%);
margin-right: -13px;
@ -175,10 +181,12 @@
left: 100%;
top: 50%;
}
.hotspot-popover.arrow-right:after {
border-left-color: #fff;
margin-top: -12px;
}
.hotspot-popover.arrow-right:before {
border-left-color: hsl(0, 0%, 80%);
margin-top: -13px;

View File

@ -706,7 +706,6 @@ nav ul li.active::after {
background: linear-gradient(0deg, #fff 0%, transparent 40%);
}
.portico-landing.hello .hero .waves {
position: absolute;
@ -759,9 +758,11 @@ nav ul li.active::after {
0% {
transform: rotate(0);
}
50% {
transform: rotate(180deg);
}
100% {
transform: rotate(360deg);
}
@ -771,9 +772,11 @@ nav ul li.active::after {
0% {
transform: rotate(0);
}
50% {
transform: rotate(-180deg);
}
100% {
transform: rotate(-360deg);
}
@ -1335,7 +1338,6 @@ nav ul li.active::after {
font-weight: 400;
}
.portico-landing.hello .testimonials blockquote {
padding: 0px;
@ -1626,6 +1628,7 @@ nav ul li.active::after {
.portico-landing.apps {
padding-top: 0px;
}
.portico-landing.apps .main,
.portico-landing.features-app .main {
background-color: #fff;
@ -1887,7 +1890,6 @@ nav ul li.active::after {
text-align: left;
}
.portico-landing.hello .apps .screen.ios {
width: 200px;
height: 350px;
@ -2194,7 +2196,6 @@ nav ul li.active::after {
color: hsla(216, 23%, 13%, 0.5);
}
/* -- integration instructions -- */
.portico-landing.integrations #integration-instructions-group {
@ -2479,7 +2480,6 @@ nav ul li.active::after {
top: -220px;
}
.pricing-overlay.pricing-model .pricing-container {
padding: 50px;
text-align: left;
@ -2568,9 +2568,11 @@ nav ul li.active::after {
0% {
box-shadow: 0px 0px 0px rgba(106, 201, 185, 0);
}
50% {
box-shadow: 0px 0px 25px rgba(106, 201, 185, 0.8);
}
100% {
box-shadow: 0px 0px 0px rgba(106, 201, 185, 0);
}
@ -3061,7 +3063,6 @@ nav ul li.active::after {
min-height: 0px;
}
.portico-landing.apps .other-apps {
padding: 50px 5px 120px 5px;
}
@ -3334,7 +3335,6 @@ nav ul li.active::after {
.gradients .gradient.sunburst {
background: linear-gradient(5deg, transparent 20%, #e8d275 80%);
}
}
@media (max-width: 375px) {

View File

@ -25,15 +25,15 @@
background-color: #FFF;
background-image:
-moz-linear-gradient(45deg, hsl(0, 0%, 80%) 25%, transparent 25%),
-moz-linear-gradient(-45deg, hsl(0, 0%, 80%) 25%, transparent 25%),
-moz-linear-gradient(45deg, transparent 75%, hsl(0, 0%, 0%) 75%),
-moz-linear-gradient(-45deg, transparent 75%, hsl(0, 0%, 0%) 75%);
-moz-linear-gradient(45deg, hsl(0, 0%, 80%) 25%, transparent 25%),
-moz-linear-gradient(-45deg, hsl(0, 0%, 80%) 25%, transparent 25%),
-moz-linear-gradient(45deg, transparent 75%, hsl(0, 0%, 0%) 75%),
-moz-linear-gradient(-45deg, transparent 75%, hsl(0, 0%, 0%) 75%);
background-image:
-webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, hsl(0, 0%, 80%)), color-stop(.25, transparent)),
-webkit-gradient(linear, 0 0, 100% 100%, color-stop(.25, hsl(0, 0%, 80%)), color-stop(.25, transparent)),
-webkit-gradient(linear, 0 100%, 100% 0, color-stop(.75, transparent), color-stop(.75, hsl(0, 0%, 80%))),
-webkit-gradient(linear, 0 0, 100% 100%, color-stop(.75, transparent), color-stop(.75, hsl(0, 0%, 80%)));
-webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, hsl(0, 0%, 80%)), color-stop(.25, transparent)),
-webkit-gradient(linear, 0 0, 100% 100%, color-stop(.25, hsl(0, 0%, 80%)), color-stop(.25, transparent)),
-webkit-gradient(linear, 0 100%, 100% 0, color-stop(.75, transparent), color-stop(.75, hsl(0, 0%, 80%))),
-webkit-gradient(linear, 0 0, 100% 100%, color-stop(.75, transparent), color-stop(.75, hsl(0, 0%, 80%)));
-moz-background-size: 20px 20px;
background-size: 20px 20px;

View File

@ -5,7 +5,6 @@
/* This max-width must be synced with message_viewport.is_narrow */
@media (max-width: 975px) {
.screen-full-show {
display: none !important;
}
@ -46,7 +45,6 @@
width: 30px;
}
#top_navbar.rightside-userlist .navbar-search {
margin-right: 100px;
}
@ -59,7 +57,6 @@
margin-right: 60px;
}
#userlist-toggle {
display: block;
}
@ -91,11 +88,9 @@
left: 300px;
right: 50px;
}
}
@media (max-width: 775px) {
body {
padding: 0px;
}
@ -192,7 +187,6 @@
#subscriptions-status {
left: 35px;
}
}
@media (max-width: 500px) {
@ -341,6 +335,7 @@
html {
overflow-x: hidden;
}
.stream_row .description {
display: none;
}

View File

@ -202,7 +202,6 @@ html {
stroke: #fff !important;
}
.new-style {
-webkit-font-smoothing: antialiased;
}
@ -491,7 +490,6 @@ html {
width: 328px;
}
.split-view .org-header .avatar,
.register-page-container .org-header .avatar {
display: inline-block;
@ -630,6 +628,7 @@ button.login-google-button {
.forgot-password-container .actions {
line-height: 0;
}
.forgot-password-container .actions .back-to-login i {
position: relative;
top: 5px;
@ -691,7 +690,6 @@ button.login-google-button {
margin-left: 2px;
}
#registration [for="realm_in_root_domain"] {
font-weight: normal !important;
}

View File

@ -299,6 +299,7 @@ body {
.markdown ol > li > p:not(:first-child) {
padding-left: 30px;
}
.markdown ul > li:before {
content: none;
}
@ -598,7 +599,6 @@ input.text-error {
line-height: 0.8;
}
.header-main .logo .light {
display: inline-block;
vertical-align: top;
@ -1758,7 +1758,6 @@ input.new-organization-button {
.footer-navigation {
margin-left: 0px;
}
}
@media (max-width: 815px) {
@ -1767,6 +1766,7 @@ input.new-organization-button {
margin-left: auto;
margin-right: auto;
}
.footer section {
width: calc(50% - 40px);
}
@ -1916,7 +1916,6 @@ input.new-organization-button {
padding-left: 6px;
padding-right: 6px;
}
}
@media (max-width: 475px) {

View File

@ -254,6 +254,7 @@ td .button {
font-family: FontAwesome, "Yantramanav", Source Sans Pro;
font-weight: 600;
}
.dynamic-input {
display: inline-block;
padding: 5px;
@ -468,7 +469,6 @@ input[type=checkbox].inline-block {
}
@media (max-width: 480px) {
#pw_strength {
margin: auto;
}
@ -508,13 +508,13 @@ input[type=checkbox].inline-block {
margin: auto;
text-align: center;
}
}
#organization .settings-section .settings-section-icon,
#settings .settings-section .settings-section-icon {
margin-right: 8px;
}
#notification-settings .notification-reminder {
text-align: left;
}
@ -899,6 +899,7 @@ input[type=checkbox].inline-block {
text-decoration: none;
margin-right: 5px;
}
/* -- new settings overlay -- */
#settings_page {
height: 95vh;
@ -1115,7 +1116,6 @@ input[type=checkbox].inline-block {
cursor: pointer;
}
#deactivation_user_modal.fade.in {
top: calc(50% - 120px);
}
@ -1235,7 +1235,7 @@ input[type=text]#settings_search {
position: absolute;
bottom: 0px;
}
/* -- end new settings overlay -- */
@media (max-width: 953px) {
.user-avatar-section,
.realm-icon-section {

View File

@ -183,6 +183,7 @@ hr {
.center-charts {
width: calc(816px * 2); /* 790px + 4px for borders + 2px for il-block + 20px margins */
}
.center-charts .left,
.center-charts .right {
display: inline-block;

View File

@ -133,9 +133,11 @@ p.n-margin {
0% {
box-shadow: 0px 0px 30px hsla(0, 0%, 0%, 0.35);
}
50% {
box-shadow: 0px 0px 30px hsla(0, 0%, 0%, 0.15);
}
100% {
box-shadow: 0px 0px 30px hsla(0, 0%, 0%, 0.35);
}
@ -299,7 +301,6 @@ input {
}
/* Override Bootstrap's fixed sizes for various elements */
textarea,
label {
font-size: inherit;
@ -307,7 +308,6 @@ label {
}
/* List of text-like input types taken from Bootstrap */
input[type="text"],
input[type="password"],
input[type="datetime"],
@ -900,6 +900,7 @@ td.pointer {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
}
@ -909,6 +910,7 @@ td.pointer {
from {
-moz-transform: rotate(0deg);
}
to {
-moz-transform: rotate(359deg);
}
@ -917,7 +919,9 @@ td.pointer {
@-ms-keyframes rotate {
from {
-ms-transform: rotate(0deg);
}
to {
-ms-transform: rotate(359deg);
}
@ -927,6 +931,7 @@ td.pointer {
from {
-o-transform: rotate(0deg);
}
to {
-o-transform: rotate(359deg);
}
@ -936,6 +941,7 @@ td.pointer {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
@ -945,6 +951,7 @@ td.pointer {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
@ -954,6 +961,7 @@ td.pointer {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
@ -963,6 +971,7 @@ td.pointer {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
@ -972,6 +981,7 @@ td.pointer {
0% {
transform: translateX(-10px);
}
100% {
transform: translateX(0px);
}
@ -981,6 +991,7 @@ td.pointer {
0% {
transform: translateX(-10px);
}
100% {
transform: translateX(0px);
}
@ -990,6 +1001,7 @@ td.pointer {
0% {
transform: translateX(-10px);
}
100% {
transform: translateX(0px);
}
@ -1418,6 +1430,7 @@ div.focused_table {
max-height: 8.5em;
overflow: hidden;
}
.message_content.collapsed {
max-height: 0em;
overflow: hidden;
@ -1660,7 +1673,6 @@ blockquote p {
padding-right: 2px;
}
#tab_list .root a:hover {
color: hsl(0, 0%, 0%);
}
@ -1792,7 +1804,6 @@ blockquote p {
z-index: 5;
}
nav .column-left {
text-align: center;
}
@ -1853,7 +1864,6 @@ nav a .no-style {
line-height: 27px;
}
#search_query:focus {
box-shadow: inset 0px 0px 0px 2px hsl(204, 20%, 74%);
}
@ -2101,6 +2111,7 @@ div.floating_recipient {
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
color: maroon;
}
.operator {
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
}
@ -2473,10 +2484,12 @@ button.topic_edit_cancel {
#message_edit_form {
margin-bottom: 10px;
}
#message_edit_form .edit-controls {
margin-left: 0px;
margin-top: 0px;
}
#message_edit_form textarea {
width: 100%;
min-width: 206px;
@ -2484,6 +2497,7 @@ button.topic_edit_cancel {
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#message_edit_form .control-group.no-margin {
margin-bottom: 0px;
}

View File

@ -171,12 +171,12 @@ a.button:hover {
z-index: 100;
}
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
@ -193,24 +193,30 @@ a.button:hover {
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
@ -222,6 +228,7 @@ a.button:hover {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
@ -230,6 +237,7 @@ a.button:hover {
.ExternalClass div {
line-height: 100%;
}
/* iOS converts adreses in emails to links automatically*/
.apple-link a {
color: inherit !important;

View File

@ -22,6 +22,11 @@ def validate(fn):
def check_our_files(filenames):
# type: (Iterable[str]) -> None
for filename in filenames:
if 'pygments.css' in filename:
# This just has really strange formatting that our
# parser doesn't like
continue
try:
validate(filename)
except CssParserException as e:

View File

@ -38,6 +38,17 @@ def find_end_brace(tokens, i, end):
return i
def get_whitespace(tokens, i, end):
# type: (List[Token], int, int) -> Tuple[int, str]
text = ''
while (i < end) and ws(tokens[i].s[0]):
s = tokens[i].s
text += s
i += 1
return i, text
def get_whitespace_and_comments(tokens, i, end, line=None):
# type: (List[Token], int, int, int) -> Tuple[int, str]
@ -65,6 +76,43 @@ def get_whitespace_and_comments(tokens, i, end, line=None):
return i, text
def indent_count(s):
# type: (str) -> int
return len(s) - len(s.lstrip())
def dedent_block(s):
# type: (str) -> (str)
s = s.lstrip()
lines = s.split('\n')
non_blank_lines = [line for line in lines if line]
if len(non_blank_lines) <= 1:
return s
min_indent = min(indent_count(line) for line in lines[1:])
lines = [lines[0]] + [line[min_indent:] for line in lines[1:]]
return '\n'.join(lines)
def indent_block(s):
# type: (str) -> (str)
lines = s.split('\n')
lines = [
' ' + line if line else ''
for line in lines
]
return '\n'.join(lines)
def ltrim(s):
# type: (str) -> (str)
content = s.lstrip()
padding = s[:-1 * len(content)]
s = padding.replace(' ', '')[1:] + content
return s
def rtrim(s):
# type: (str) -> (str)
content = s.rstrip()
padding = s[len(content):]
s = content + padding.replace(' ', '')[:-1]
return s
############### Begin parsing here
@ -82,7 +130,7 @@ def parse_sections(tokens, start, end):
i = find_end_brace(tokens, start, end)
section_end = i + 1
i, post_fluff = get_whitespace_and_comments(tokens, i+1, end)
i, post_fluff = get_whitespace(tokens, i+1, end)
section = parse_section(
tokens=tokens,
@ -228,7 +276,7 @@ def parse_declaration(tokens, start, end):
semicolon = (i < end) and (tokens[i].s == ';')
if semicolon:
i += 1
_, post_fluff = get_whitespace_and_comments(tokens, i, end)
_, post_fluff = get_whitespace_and_comments(tokens, i, end, line=tokens[i].line)
declaration = CssDeclaration(
tokens=tokens,
pre_fluff=pre_fluff,
@ -254,100 +302,6 @@ def parse_value(tokens, start, end):
post_fluff=post_fluff,
)
def handle_prefluff(pre_fluff, indent=False):
# type: (str, bool) -> str
pre_fluff_lines = pre_fluff.split('\n')
formatted_pre_fluff_lines = []
comment_indent = ''
general_indent = ''
if indent:
general_indent = ' '
for i, ln in enumerate(pre_fluff_lines):
line_indent = ''
if ln.strip() != '':
if not i:
line_indent = general_indent
comment_indent = ' '
else:
if comment_indent:
if ('*/' in ln or '*' in ln) and (ln.strip()[:2] in ('*/', '* ', '*')):
line_indent = general_indent
if '*/' in ln:
comment_indent = ''
else:
line_indent = general_indent + comment_indent
else:
line_indent = general_indent
comment_indent = ' '
elif len(pre_fluff_lines) == 1 and indent and ln != '':
line_indent = ' '
formatted_pre_fluff_lines.append(line_indent + ln.strip())
if formatted_pre_fluff_lines[-1] != '':
if formatted_pre_fluff_lines[-1].strip() == '' and indent:
formatted_pre_fluff_lines[-1] = ''
formatted_pre_fluff_lines.append('')
pre_fluff = '\n'.join(formatted_pre_fluff_lines)
res = ''
if indent:
if '\n' in pre_fluff:
res = pre_fluff + ' '
elif pre_fluff == '':
res = ' '
else:
res = pre_fluff.rstrip() + ' '
else:
res = pre_fluff
return res
def handle_postfluff(post_fluff, indent=False, space_after_first_line=False):
# type: (str, bool, bool) -> str
post_fluff_lines = post_fluff.split('\n')
formatted_post_fluff_lines = []
comment_indent = ''
general_indent = ''
if indent:
general_indent = ' '
for i, ln in enumerate(post_fluff_lines):
line_indent = ''
if ln.strip() != '':
if i:
if comment_indent:
if ('*/' in ln or '*' in ln) and (ln.strip()[:2] in ('*/', '* ', '*')):
line_indent = general_indent
if '*/' in ln:
comment_indent = ''
else:
line_indent = general_indent + comment_indent
else:
line_indent = general_indent
comment_indent = ' '
elif indent and not i and len(post_fluff_lines) > 2:
formatted_post_fluff_lines.append('')
line_indent = general_indent
comment_indent = ' '
elif space_after_first_line:
line_indent = ' '
if not i:
comment_indent = ' '
elif not i:
comment_indent = ' '
formatted_post_fluff_lines.append(line_indent + ln.strip())
if len(formatted_post_fluff_lines) == 1 and not space_after_first_line:
if formatted_post_fluff_lines[-1].strip() == '':
if formatted_post_fluff_lines[-1] != '':
formatted_post_fluff_lines[-1] = ' '
else:
formatted_post_fluff_lines.append('')
elif formatted_post_fluff_lines[-1].strip() == '':
formatted_post_fluff_lines[-1] = ''
if len(formatted_post_fluff_lines) == 1 and indent:
formatted_post_fluff_lines.append('')
elif space_after_first_line:
formatted_post_fluff_lines.append('')
post_fluff = '\n'.join(formatted_post_fluff_lines)
return post_fluff
#### Begin CSS classes here
class CssSectionList:
@ -358,7 +312,7 @@ class CssSectionList:
def text(self):
# type: () -> str
res = ''.join(section.text() for section in self.sections)
res = '\n\n'.join(section.text().strip() for section in self.sections) + '\n'
return res
class CssNestedSection:
@ -373,19 +327,12 @@ class CssNestedSection:
def text(self):
# type: () -> str
res = ''
res += self.pre_fluff
res += self.selector_list.text()
res += ' {'
section_list_lines = self.section_list.text().split('\n')
formatted_section_list = []
for ln in section_list_lines:
if ln.strip() == '':
formatted_section_list.append('')
else:
formatted_section_list.append(' ' + ln)
res += '\n'.join(formatted_section_list)
res += '}'
res += self.post_fluff
res += ltrim(self.pre_fluff)
res += self.selector_list.text().strip()
res += ' {\n'
res += indent_block(self.section_list.text().strip())
res += '\n}'
res += rtrim(self.post_fluff)
return res
class CssSection:
@ -400,11 +347,14 @@ class CssSection:
def text(self):
# type: () -> str
res = ''
res += handle_prefluff(self.pre_fluff)
res += self.selector_list.text()
res += rtrim(dedent_block(self.pre_fluff))
if res:
res += '\n'
res += self.selector_list.text().strip()
res += ' '
res += self.declaration_block.text()
res += handle_postfluff(self.post_fluff, space_after_first_line=True)
res += '\n'
res += rtrim(self.post_fluff)
return res
class CssSelectorList:
@ -438,9 +388,9 @@ class CssDeclarationBlock:
def text(self):
# type: () -> str
res = '{'
res = '{\n'
for declaration in self.declarations:
res += declaration.text()
res += ' ' + declaration.text()
res += '}'
return res
@ -457,18 +407,23 @@ class CssDeclaration:
def text(self):
# type: () -> str
res = ''
res += handle_prefluff(self.pre_fluff, True)
res += ltrim(self.pre_fluff).rstrip()
if res:
res += '\n '
res += self.css_property
res += ':'
value_text = self.css_value.text()
if '\n' in value_text:
# gradient values can be multi-line
res += value_text.rstrip()
value_text = self.css_value.text().rstrip()
if value_text.startswith('\n'):
res += value_text
elif '\n' in value_text:
res += ' '
res += ltrim(value_text)
else:
res += ' '
res += value_text.strip()
res += ';'
res += handle_postfluff(self.post_fluff, True, True)
res += rtrim(self.post_fluff)
res += '\n'
return res
class CssValue:

View File

@ -9,8 +9,6 @@ try:
CssParserException,
CssSection,
parse,
handle_prefluff,
handle_postfluff
)
except ImportError:
print('ERROR!!! You need to run this via tools/test-tools.')
@ -25,7 +23,7 @@ class ParserTestHappyPath(unittest.TestCase):
}'''
my_css = my_selector + ' ' + my_block
res = parse(my_css)
self.assertEqual(res.text(), 'li.foo {\n color: red;\n}')
self.assertEqual(res.text().strip(), 'li.foo {\n color: red;\n}')
section = cast(CssSection, res.sections[0])
block = section.declaration_block
self.assertEqual(block.text().strip(), '{\n color: red;\n}')
@ -54,11 +52,11 @@ class ParserTestHappyPath(unittest.TestCase):
p { color: red }
'''
reformatted_css = '\np {\n color: red;\n}\n'
reformatted_css = 'p {\n color: red;\n}'
res = parse(my_css)
self.assertEqual(res.text(), reformatted_css)
self.assertEqual(res.text().strip(), reformatted_css)
section = cast(CssSection, res.sections[0])
@ -86,26 +84,6 @@ class ParserTestHappyPath(unittest.TestCase):
selectors = section.selector_list.selectors
self.assertEqual(len(selectors), 3)
def test_comment_at_end(self):
# type: () -> None
'''
This test verifies the current behavior, which is to
attach comments to the preceding rule, but we should
probably change it so the comments gets attached to
the next block, if possible.
'''
my_css = '''
p {
color: black;
}
/* comment at the end of the text */
'''
res = parse(my_css)
self.assertEqual(len(res.sections), 1)
section = res.sections[0]
self.assertIn('comment at the end', section.post_fluff)
def test_media_block(self):
# type: () -> None
my_css = '''
@ -116,39 +94,8 @@ class ParserTestHappyPath(unittest.TestCase):
}'''
res = parse(my_css)
self.assertEqual(len(res.sections), 1)
self.assertEqual(res.text(), '\n @media (max-width: 300px) {\n h5 {\n margin: 0;\n }\n}')
def test_handle_prefluff(self):
# type: () -> None
PREFLUFF = ' \n '
PREFLUFF1 = ' '
PREFLUFF2 = ' /* some comment \nhere */'
PREFLUFF3 = '\n /* some comment \nhere */'
self.assertEqual(handle_prefluff(PREFLUFF), '\n')
self.assertEqual(handle_prefluff(PREFLUFF, True), '\n ')
self.assertEqual(handle_prefluff(PREFLUFF1), '')
self.assertEqual(handle_prefluff(PREFLUFF1, True), '\n ')
self.assertEqual(handle_prefluff(PREFLUFF2), '/* some comment\n here */\n')
self.assertEqual(handle_prefluff(PREFLUFF3, True), '\n /* some comment\n here */\n ')
def test_handle_postfluff(self):
# type: () -> None
POSTFLUFF = '/* Comment Here */'
POSTFLUFF1 = '/* Comment \nHere */'
POSTFLUFF2 = ' '
POSTFLUFF3 = '\n /* some comment \nhere */'
self.assertEqual(handle_postfluff(POSTFLUFF), '/* Comment Here */\n')
self.assertEqual(handle_postfluff(POSTFLUFF, space_after_first_line=True), ' /* Comment Here */\n')
self.assertEqual(handle_postfluff(POSTFLUFF, indent=True, space_after_first_line=True), ' /* Comment Here */\n')
self.assertEqual(handle_postfluff(POSTFLUFF1), '/* Comment\n Here */')
self.assertEqual(handle_postfluff(POSTFLUFF1, space_after_first_line=True), ' /* Comment\n Here */\n')
self.assertEqual(handle_postfluff(POSTFLUFF1, indent=True, space_after_first_line=True), ' /* Comment\n Here */\n')
self.assertEqual(handle_postfluff(POSTFLUFF2), '')
self.assertEqual(handle_postfluff(POSTFLUFF2, space_after_first_line=True), '')
self.assertEqual(handle_postfluff(POSTFLUFF2, indent=True, space_after_first_line=True), '\n')
self.assertEqual(handle_postfluff(POSTFLUFF3), '\n/* some comment\n here */')
self.assertEqual(handle_postfluff(POSTFLUFF3, space_after_first_line=True), '\n/* some comment\n here */\n')
self.assertEqual(handle_postfluff(POSTFLUFF3, indent=True, space_after_first_line=True), '\n /* some comment\n here */\n')
expected = '@media (max-width: 300px) {\n h5 {\n margin: 0;\n }\n}'
self.assertEqual(res.text().strip(), expected)
class ParserTestSadPath(unittest.TestCase):
'''