mirror of
https://github.com/beautifier/js-beautify.git
synced 2024-11-26 22:20:56 +00:00
Bring css python implementation into sync
This commit is contained in:
parent
698dd05fc2
commit
77630e5005
@ -77,33 +77,6 @@ CSS beautifier (http://jsbeautifier.org/)
|
||||
return 0
|
||||
|
||||
|
||||
class Printer:
|
||||
|
||||
def __init__(self, indent_char, indent_size, default_indent=""):
|
||||
self.singleIndent = (indent_size) * indent_char
|
||||
self.indentLevel = 0
|
||||
self.nestedLevel = 0
|
||||
|
||||
self.baseIndentString = default_indent
|
||||
self.output = Output(self.singleIndent, self.baseIndentString)
|
||||
|
||||
def indent(self):
|
||||
self.indentLevel += 1
|
||||
|
||||
def outdent(self):
|
||||
if self.indentLevel > 0:
|
||||
self.indentLevel -= 1
|
||||
|
||||
def preserveSingleSpace(self, isAfterSpace):
|
||||
if isAfterSpace:
|
||||
self.output.space_before_token = True
|
||||
|
||||
def print_string(self, output_string):
|
||||
if self.output.just_added_newline():
|
||||
self.output.set_indent(self.indentLevel)
|
||||
|
||||
self.output.add_token(output_string)
|
||||
|
||||
|
||||
class Beautifier:
|
||||
|
||||
@ -111,7 +84,7 @@ class Beautifier:
|
||||
# in javascript, these two differ
|
||||
# in python they are the same, different methods are called on them
|
||||
# IMPORTANT: This string must be run through six to handle \u chars
|
||||
self.lineBreak = re.compile(six.u(r"\r\n|[\n\r\u2028\u2029]"))
|
||||
self.lineBreak = re.compile(six.u(r"\r\n|[\n\r]"))
|
||||
self.allLineBreaks = self.lineBreak
|
||||
|
||||
self.comment_pattern = re.compile(
|
||||
@ -124,14 +97,13 @@ class Beautifier:
|
||||
|
||||
self.__source_text = source_text
|
||||
|
||||
opts = BeautifierOptions(opts)
|
||||
|
||||
self.opts = opts
|
||||
self.indentSize = opts.indent_size
|
||||
self.indentChar = opts.indent_char
|
||||
self.input = None
|
||||
self.ch = None
|
||||
self._options = BeautifierOptions(opts)
|
||||
self._input = None
|
||||
self._ch = None
|
||||
|
||||
self._indentLevel = 0
|
||||
self._nestedLevel = 0
|
||||
self._output = None
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
|
||||
# also in CONDITIONAL_GROUP_RULE below
|
||||
@ -149,14 +121,14 @@ class Beautifier:
|
||||
|
||||
def eatString(self, endChars):
|
||||
result = ''
|
||||
self.ch = self.input.next()
|
||||
while self.ch:
|
||||
result += self.ch
|
||||
if self.ch == "\\":
|
||||
result += self.input.next()
|
||||
elif self.ch in endChars or self.ch == "\n":
|
||||
self._ch = self._input.next()
|
||||
while self._ch:
|
||||
result += self._ch
|
||||
if self._ch == "\\":
|
||||
result += self._input.next()
|
||||
elif self._ch in endChars or self._ch == "\n":
|
||||
break
|
||||
self.ch = self.input.next()
|
||||
self._ch = self._input.next()
|
||||
return result
|
||||
|
||||
# Skips any white space in the source text from the current position.
|
||||
@ -164,15 +136,15 @@ class Beautifier:
|
||||
# newline character found; if the user has preserve_newlines off, only
|
||||
# the first newline will be output
|
||||
def eatWhitespace(self, allowAtLeastOneNewLine=False):
|
||||
result = whitespaceChar.search(self.input.peek() or '') is not None
|
||||
result = whitespaceChar.search(self._input.peek() or '') is not None
|
||||
isFirstNewLine = True
|
||||
|
||||
while whitespaceChar.search(self.input.peek() or '') is not None:
|
||||
self.ch = self.input.next()
|
||||
if allowAtLeastOneNewLine and self.ch == "\n":
|
||||
if self.opts.preserve_newlines or isFirstNewLine:
|
||||
while whitespaceChar.search(self._input.peek() or '') is not None:
|
||||
self._ch = self._input.next()
|
||||
if allowAtLeastOneNewLine and self._ch == "\n":
|
||||
if self._options.preserve_newlines or isFirstNewLine:
|
||||
isFirstNewLine = False
|
||||
self.output.add_new_line(True)
|
||||
self._output.add_new_line(True)
|
||||
return result
|
||||
|
||||
# Nested pseudo-class if we are insideRule
|
||||
@ -181,7 +153,7 @@ class Beautifier:
|
||||
def foundNestedPseudoClass(self):
|
||||
openParen = 0
|
||||
i = 1
|
||||
ch = self.input.peek(i)
|
||||
ch = self._input.peek(i)
|
||||
while ch:
|
||||
if ch == "{":
|
||||
return True
|
||||
@ -195,20 +167,39 @@ class Beautifier:
|
||||
elif ch == ";" or ch == "}":
|
||||
return False
|
||||
i += 1
|
||||
ch = self.input.peek(i)
|
||||
ch = self._input.peek(i)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def indent(self):
|
||||
self._indentLevel += 1
|
||||
|
||||
def outdent(self):
|
||||
if self._indentLevel > 0:
|
||||
self._indentLevel -= 1
|
||||
|
||||
def preserveSingleSpace(self, isAfterSpace):
|
||||
if isAfterSpace:
|
||||
self._output.space_before_token = True
|
||||
|
||||
def print_string(self, output_string):
|
||||
if self._output.just_added_newline():
|
||||
self._output.set_indent(self._indentLevel)
|
||||
|
||||
self._output.add_token(output_string)
|
||||
|
||||
|
||||
def beautify(self):
|
||||
if self.opts.disabled:
|
||||
if self._options.disabled:
|
||||
return self.__source_text
|
||||
|
||||
source_text = self.__source_text
|
||||
|
||||
if self.opts.eol == 'auto':
|
||||
self.opts.eol = '\n'
|
||||
if self._options.eol == 'auto':
|
||||
self._options.eol = '\n'
|
||||
if self.lineBreak.search(source_text or ''):
|
||||
self.opts.eol = self.lineBreak.search(source_text).group()
|
||||
self._options.eol = self.lineBreak.search(source_text).group()
|
||||
|
||||
|
||||
# HACK: newline parsing inconsistent. This brute force normalizes the
|
||||
@ -216,19 +207,16 @@ class Beautifier:
|
||||
source_text = re.sub(self.allLineBreaks, '\n', source_text)
|
||||
|
||||
m = re.search("^[\t ]*", source_text)
|
||||
self.baseIndentString = m.group(0)
|
||||
baseIndentString = m.group(0)
|
||||
|
||||
printer = Printer(
|
||||
self.indentChar,
|
||||
self.indentSize,
|
||||
self.baseIndentString)
|
||||
self.output = printer.output
|
||||
self.input = InputScanner(source_text)
|
||||
self._output = Output(self._options.indent_string, baseIndentString)
|
||||
|
||||
output = self.output
|
||||
input = self.input
|
||||
self._input = InputScanner(source_text)
|
||||
|
||||
self.ch = None
|
||||
self._indentLevel = 0
|
||||
self._nestedLevel = 0
|
||||
|
||||
self._ch = None
|
||||
parenLevel = 0
|
||||
|
||||
insideRule = False
|
||||
@ -236,27 +224,27 @@ class Beautifier:
|
||||
enteringConditionalGroup = False
|
||||
insideAtExtend = False
|
||||
insideAtImport = False
|
||||
topCharacter = self.ch
|
||||
topCharacter = self._ch
|
||||
|
||||
while True:
|
||||
whitespace = input.read(whitespacePattern)
|
||||
whitespace = self._input.read(whitespacePattern)
|
||||
isAfterSpace = whitespace != ''
|
||||
previous_ch = topCharacter
|
||||
self.ch = input.next()
|
||||
topCharacter = self.ch
|
||||
self._ch = self._input.next()
|
||||
topCharacter = self._ch
|
||||
|
||||
if not self.ch:
|
||||
if not self._ch:
|
||||
break
|
||||
elif self.ch == '/' and input.peek() == '*':
|
||||
elif self._ch == '/' and self._input.peek() == '*':
|
||||
# /* css comment */
|
||||
# Always start block comments on a new line.
|
||||
# This handles scenarios where a block comment immediately
|
||||
# follows a property definition on the same line or where
|
||||
# minified code is being beautified.
|
||||
output.add_new_line()
|
||||
input.back()
|
||||
printer.print_string(
|
||||
input.read(
|
||||
self._output.add_new_line()
|
||||
self._input.back()
|
||||
self.print_string(
|
||||
self._input.read(
|
||||
self.block_comment_pattern))
|
||||
|
||||
# Ensures any new lines following the comment are preserved
|
||||
@ -264,27 +252,27 @@ class Beautifier:
|
||||
|
||||
# Block comments are followed by a new line so they don't
|
||||
# share a line with other properties
|
||||
output.add_new_line()
|
||||
elif self.ch == '/' and input.peek() == '/':
|
||||
self._output.add_new_line()
|
||||
elif self._ch == '/' and self._input.peek() == '/':
|
||||
# // single line comment
|
||||
# Preserves the space before a comment
|
||||
# on the same line as a rule
|
||||
output.space_before_token = True
|
||||
input.back()
|
||||
printer.print_string(input.read(self.comment_pattern))
|
||||
self._output.space_before_token = True
|
||||
self._input.back()
|
||||
self.print_string(self._input.read(self.comment_pattern))
|
||||
|
||||
# Ensures any new lines following the comment are preserved
|
||||
self.eatWhitespace(True)
|
||||
elif self.ch == '@':
|
||||
printer.preserveSingleSpace(isAfterSpace)
|
||||
elif self._ch == '@':
|
||||
self.preserveSingleSpace(isAfterSpace)
|
||||
|
||||
# deal with less propery mixins @{...}
|
||||
if input.peek() == '{':
|
||||
printer.print_string(self.ch + self.eatString('}'))
|
||||
if self._input.peek() == '{':
|
||||
self.print_string(self._ch + self.eatString('}'))
|
||||
else:
|
||||
printer.print_string(self.ch)
|
||||
self.print_string(self._ch)
|
||||
# strip trailing space, for hash property check
|
||||
variableOrRule = input.peekUntilAfter(
|
||||
variableOrRule = self._input.peekUntilAfter(
|
||||
re.compile(r"[: ,;{}()[\]\/='\"]"))
|
||||
|
||||
if variableOrRule[-1] in ": ":
|
||||
@ -293,8 +281,8 @@ class Beautifier:
|
||||
variableOrRule = self.eatString(": ")
|
||||
if variableOrRule[-1].isspace():
|
||||
variableOrRule = variableOrRule[:-1]
|
||||
printer.print_string(variableOrRule)
|
||||
output.space_before_token = True
|
||||
self.print_string(variableOrRule)
|
||||
self._output.space_before_token = True
|
||||
|
||||
if variableOrRule[-1].isspace():
|
||||
variableOrRule = variableOrRule[:-1]
|
||||
@ -306,75 +294,75 @@ class Beautifier:
|
||||
|
||||
# might be a nesting at-rule
|
||||
if variableOrRule in self.NESTED_AT_RULE:
|
||||
printer.nestedLevel += 1
|
||||
self._nestedLevel += 1
|
||||
if variableOrRule in self.CONDITIONAL_GROUP_RULE:
|
||||
enteringConditionalGroup = True
|
||||
elif not insideRule and parenLevel == 0 and \
|
||||
variableOrRule[-1] == ":":
|
||||
insidePropertyValue = True
|
||||
printer.indent()
|
||||
elif self.ch == '#' and input.peek() == '{':
|
||||
printer.preserveSingleSpace(isAfterSpace)
|
||||
printer.print_string(self.ch + self.eatString('}'))
|
||||
elif self.ch == '{':
|
||||
self.indent()
|
||||
elif self._ch == '#' and self._input.peek() == '{':
|
||||
self.preserveSingleSpace(isAfterSpace)
|
||||
self.print_string(self._ch + self.eatString('}'))
|
||||
elif self._ch == '{':
|
||||
if insidePropertyValue:
|
||||
insidePropertyValue = False
|
||||
printer.outdent()
|
||||
printer.indent()
|
||||
output.space_before_token = True
|
||||
printer.print_string(self.ch)
|
||||
self.outdent()
|
||||
self.indent()
|
||||
self._output.space_before_token = True
|
||||
self.print_string(self._ch)
|
||||
|
||||
# when entering conditional groups, only rulesets are
|
||||
# allowed
|
||||
if enteringConditionalGroup:
|
||||
enteringConditionalGroup = False
|
||||
insideRule = printer.indentLevel > printer.nestedLevel
|
||||
insideRule = self._indentLevel > self._nestedLevel
|
||||
else:
|
||||
# otherwise, declarations are also allowed
|
||||
insideRule = printer.indentLevel >= printer.nestedLevel
|
||||
insideRule = self._indentLevel >= self._nestedLevel
|
||||
|
||||
if self.opts.newline_between_rules and insideRule:
|
||||
if output.previous_line and \
|
||||
not output.previous_line.is_empty() and \
|
||||
output.previous_line.item(-1) != '{':
|
||||
output.ensure_empty_line_above('/', ',')
|
||||
if self._options.newline_between_rules and insideRule:
|
||||
if self._output.previous_line and \
|
||||
not self._output.previous_line.is_empty() and \
|
||||
self._output.previous_line.item(-1) != '{':
|
||||
self._output.ensure_empty_line_above('/', ',')
|
||||
self.eatWhitespace(True)
|
||||
output.add_new_line()
|
||||
elif self.ch == '}':
|
||||
printer.outdent()
|
||||
output.add_new_line()
|
||||
self._output.add_new_line()
|
||||
elif self._ch == '}':
|
||||
self.outdent()
|
||||
self._output.add_new_line()
|
||||
if previous_ch == '{':
|
||||
output.trim(True)
|
||||
self._output.trim(True)
|
||||
insideAtExtend = False
|
||||
insideAtImport = False
|
||||
if insidePropertyValue:
|
||||
printer.outdent()
|
||||
self.outdent()
|
||||
insidePropertyValue = False
|
||||
printer.print_string(self.ch)
|
||||
self.print_string(self._ch)
|
||||
insideRule = False
|
||||
if printer.nestedLevel:
|
||||
printer.nestedLevel -= 1
|
||||
if self._nestedLevel:
|
||||
self._nestedLevel -= 1
|
||||
|
||||
self.eatWhitespace(True)
|
||||
output.add_new_line()
|
||||
self._output.add_new_line()
|
||||
|
||||
if self.opts.newline_between_rules and \
|
||||
not output.just_added_blankline():
|
||||
if input.peek() != '}':
|
||||
output.add_new_line(True)
|
||||
elif self.ch == ":":
|
||||
if self._options.newline_between_rules and \
|
||||
not self._output.just_added_blankline():
|
||||
if self._input.peek() != '}':
|
||||
self._output.add_new_line(True)
|
||||
elif self._ch == ":":
|
||||
if (insideRule or enteringConditionalGroup) and \
|
||||
not (input.lookBack('&') or
|
||||
not (self._input.lookBack('&') or
|
||||
self.foundNestedPseudoClass()) and \
|
||||
not input.lookBack('(') and not insideAtExtend:
|
||||
not self._input.lookBack('(') and not insideAtExtend:
|
||||
# 'property: value' delimiter
|
||||
# which could be in a conditional group query
|
||||
printer.print_string(":")
|
||||
self.print_string(":")
|
||||
if not insidePropertyValue:
|
||||
insidePropertyValue = True
|
||||
output.space_before_token = True
|
||||
self._output.space_before_token = True
|
||||
self.eatWhitespace(True)
|
||||
printer.indent()
|
||||
self.indent()
|
||||
|
||||
else:
|
||||
# sass/less parent reference don't use a space
|
||||
@ -382,93 +370,93 @@ class Beautifier:
|
||||
|
||||
# preserve space before pseudoclasses/pseudoelements,
|
||||
# as it means "in any child"
|
||||
if input.lookBack(' '):
|
||||
output.space_before_token = True
|
||||
if input.peek() == ":":
|
||||
if self._input.lookBack(' '):
|
||||
self._output.space_before_token = True
|
||||
if self._input.peek() == ":":
|
||||
# pseudo-element
|
||||
self.ch = input.next()
|
||||
printer.print_string("::")
|
||||
self._ch = self._input.next()
|
||||
self.print_string("::")
|
||||
else:
|
||||
# pseudo-element
|
||||
printer.print_string(":")
|
||||
elif self.ch == '"' or self.ch == '\'':
|
||||
printer.preserveSingleSpace(isAfterSpace)
|
||||
printer.print_string(self.ch + self.eatString(self.ch))
|
||||
self.print_string(":")
|
||||
elif self._ch == '"' or self._ch == '\'':
|
||||
self.preserveSingleSpace(isAfterSpace)
|
||||
self.print_string(self._ch + self.eatString(self._ch))
|
||||
self.eatWhitespace(True)
|
||||
elif self.ch == ';':
|
||||
elif self._ch == ';':
|
||||
if insidePropertyValue:
|
||||
printer.outdent()
|
||||
self.outdent()
|
||||
insidePropertyValue = False
|
||||
insideAtExtend = False
|
||||
insideAtImport = False
|
||||
printer.print_string(self.ch)
|
||||
self.print_string(self._ch)
|
||||
self.eatWhitespace(True)
|
||||
|
||||
# This maintains single line comments on the same
|
||||
# line. Block comments are also affected, but
|
||||
# a new line is always output before one inside
|
||||
# that section
|
||||
if input.peek() is not '/':
|
||||
output.add_new_line()
|
||||
elif self.ch == '(':
|
||||
if self._input.peek() is not '/':
|
||||
self._output.add_new_line()
|
||||
elif self._ch == '(':
|
||||
# may be a url
|
||||
if input.lookBack("url"):
|
||||
printer.print_string(self.ch)
|
||||
if self._input.lookBack("url"):
|
||||
self.print_string(self._ch)
|
||||
self.eatWhitespace()
|
||||
self.ch = input.next()
|
||||
if self.ch in {')', '"', '\''}:
|
||||
input.back()
|
||||
self._ch = self._input.next()
|
||||
if self._ch in {')', '"', '\''}:
|
||||
self._input.back()
|
||||
parenLevel += 1
|
||||
elif self.ch is not None:
|
||||
printer.print_string(self.ch + self.eatString(')'))
|
||||
elif self._ch is not None:
|
||||
self.print_string(self._ch + self.eatString(')'))
|
||||
else:
|
||||
parenLevel += 1
|
||||
printer.preserveSingleSpace(isAfterSpace)
|
||||
printer.print_string(self.ch)
|
||||
self.preserveSingleSpace(isAfterSpace)
|
||||
self.print_string(self._ch)
|
||||
self.eatWhitespace()
|
||||
elif self.ch == ')':
|
||||
printer.print_string(self.ch)
|
||||
elif self._ch == ')':
|
||||
self.print_string(self._ch)
|
||||
parenLevel -= 1
|
||||
elif self.ch == ',':
|
||||
printer.print_string(self.ch)
|
||||
elif self._ch == ',':
|
||||
self.print_string(self._ch)
|
||||
self.eatWhitespace(True)
|
||||
if self.opts.selector_separator_newline and \
|
||||
if self._options.selector_separator_newline and \
|
||||
not insidePropertyValue and parenLevel < 1 and \
|
||||
not insideAtImport:
|
||||
output.add_new_line()
|
||||
self._output.add_new_line()
|
||||
else:
|
||||
output.space_before_token = True
|
||||
elif (self.ch == '>' or self.ch == '+' or self.ch == '~') and \
|
||||
self._output.space_before_token = True
|
||||
elif (self._ch == '>' or self._ch == '+' or self._ch == '~') and \
|
||||
not insidePropertyValue and parenLevel < 1:
|
||||
# handle combinator spacing
|
||||
if self.opts.space_around_combinator:
|
||||
output.space_before_token = True
|
||||
printer.print_string(self.ch)
|
||||
output.space_before_token = True
|
||||
if self._options.space_around_combinator:
|
||||
self._output.space_before_token = True
|
||||
self.print_string(self._ch)
|
||||
self._output.space_before_token = True
|
||||
else:
|
||||
printer.print_string(self.ch)
|
||||
self.print_string(self._ch)
|
||||
self.eatWhitespace()
|
||||
# squash extra whitespace
|
||||
if self.ch and bool(whitespaceChar.search(self.ch)):
|
||||
self.ch = ''
|
||||
elif self.ch == ']':
|
||||
printer.print_string(self.ch)
|
||||
elif self.ch == '[':
|
||||
printer.preserveSingleSpace(isAfterSpace)
|
||||
printer.print_string(self.ch)
|
||||
elif self.ch == '=':
|
||||
if self._ch and bool(whitespaceChar.search(self._ch)):
|
||||
self._ch = ''
|
||||
elif self._ch == ']':
|
||||
self.print_string(self._ch)
|
||||
elif self._ch == '[':
|
||||
self.preserveSingleSpace(isAfterSpace)
|
||||
self.print_string(self._ch)
|
||||
elif self._ch == '=':
|
||||
# no whitespace before or after
|
||||
self.eatWhitespace()
|
||||
printer.print_string('=')
|
||||
if bool(whitespaceChar.search(self.ch)):
|
||||
self.ch = ''
|
||||
elif self.ch == '!': # !important
|
||||
printer.print_string(' ')
|
||||
printer.print_string(self.ch)
|
||||
self.print_string('=')
|
||||
if bool(whitespaceChar.search(self._ch)):
|
||||
self._ch = ''
|
||||
elif self._ch == '!': # !important
|
||||
self.print_string(' ')
|
||||
self.print_string(self._ch)
|
||||
else:
|
||||
printer.preserveSingleSpace(isAfterSpace)
|
||||
printer.print_string(self.ch)
|
||||
self.preserveSingleSpace(isAfterSpace)
|
||||
self.print_string(self._ch)
|
||||
|
||||
sweet_code = output.get_code(self.opts.end_with_newline, self.opts.eol)
|
||||
sweet_code = self._output.get_code(self._options.end_with_newline, self._options.eol)
|
||||
|
||||
return sweet_code
|
||||
|
Loading…
Reference in New Issue
Block a user