Exemple #1
0
    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)
Exemple #2
0
    def __init__(self,
                 beautifier,
                 indent_char,
                 indent_size,
                 default_indent=""):
        self.beautifier = beautifier
        self.newlines_from_last_ws_eat = 0
        self.indentSize = indent_size
        self.singleIndent = (indent_size) * indent_char
        self.indentLevel = 0
        self.nestedLevel = 0

        self.baseIndentString = default_indent
        self.output = Output(self.singleIndent, self.baseIndentString)
Exemple #3
0
    def beautify(self):
        if self._options.disabled:
            return self.__source_text

        source_text = self.__source_text

        if self._options.eol == "auto":
            self._options.eol = "\n"
            if self.lineBreak.search(source_text or ""):
                self._options.eol = self.lineBreak.search(source_text).group()

        # HACK: newline parsing inconsistent. This brute force normalizes the
        # input newlines.
        source_text = re.sub(self.allLineBreaks, "\n", source_text)
        baseIndentString = re.search("^[\t ]*", source_text).group(0)

        self._output = Output(self._options, baseIndentString)

        self._input = InputScanner(source_text)

        self._indentLevel = 0
        self._nestedLevel = 0

        self._ch = None
        parenLevel = 0

        insideRule = False
        insidePropertyValue = False
        enteringConditionalGroup = False
        insideAtExtend = False
        insideAtImport = False
        topCharacter = self._ch

        while True:
            whitespace = self._input.read(whitespacePattern)
            isAfterSpace = whitespace != ""
            previous_ch = topCharacter
            self._ch = self._input.next()
            if self._ch == "\\" and self._input.hasNext():
                self._ch += self._input.next()
            topCharacter = self._ch

            if not self._ch:
                break
            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.
                self._output.add_new_line()
                self._input.back()
                comment = self._input.read(self.block_comment_pattern)

                # handle ignore directive
                directives = directives_core.get_directives(comment)
                if directives and directives.get("ignore") == "start":
                    comment += directives_core.readIgnored(self._input)

                self.print_string(comment)

                # Ensures any new lines following the comment are preserved
                self.eatWhitespace(True)

                # Block comments are followed by a new line so they don't
                # share a line with other properties
                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
                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 == "@":
                self.preserveSingleSpace(isAfterSpace)

                # deal with less propery mixins @{...}
                if self._input.peek() == "{":
                    self.print_string(self._ch + self.eatString("}"))
                else:
                    self.print_string(self._ch)
                    # strip trailing space, for hash property check
                    variableOrRule = self._input.peekUntilAfter(
                        re.compile(r"[: ,;{}()[\]\/='\"]"))

                    if variableOrRule[-1] in ": ":
                        # wwe have a variable or pseudo-class, add it and
                        # insert one space before continuing
                        variableOrRule = self.eatString(": ")
                        if variableOrRule[-1].isspace():
                            variableOrRule = variableOrRule[:-1]
                        self.print_string(variableOrRule)
                        self._output.space_before_token = True

                    if variableOrRule[-1].isspace():
                        variableOrRule = variableOrRule[:-1]

                    if variableOrRule == "extend":
                        insideAtExtend = True
                    elif variableOrRule == "import":
                        insideAtImport = True

                    # might be a nesting at-rule
                    if variableOrRule in self.NESTED_AT_RULE:
                        self._nestedLevel += 1
                        if variableOrRule in self.CONDITIONAL_GROUP_RULE:
                            enteringConditionalGroup = True
                    elif (not insideRule and parenLevel == 0
                          and variableOrRule[-1] == ":"):
                        insidePropertyValue = True
                        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
                    self.outdent()

                # when entering conditional groups, only rulesets are
                # allowed
                if enteringConditionalGroup:
                    enteringConditionalGroup = False
                    insideRule = self._indentLevel >= self._nestedLevel
                else:
                    # otherwise, declarations are also allowed
                    insideRule = self._indentLevel >= self._nestedLevel - 1

                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._output.space_before_token = True

                # The difference in print_string and indent order
                # is necessary to indent the '{' correctly
                if self._options.brace_style == "expand":
                    self._output.add_new_line()
                    self.print_string(self._ch)
                    self.indent()
                    self._output.set_indent(self._indentLevel)
                else:
                    self.indent()
                    self.print_string(self._ch)

                self.eatWhitespace(True)
                self._output.add_new_line()
            elif self._ch == "}":
                self.outdent()
                self._output.add_new_line()
                if previous_ch == "{":
                    self._output.trim(True)
                insideAtExtend = False
                insideAtImport = False
                if insidePropertyValue:
                    self.outdent()
                    insidePropertyValue = False
                self.print_string(self._ch)
                insideRule = False
                if self._nestedLevel:
                    self._nestedLevel -= 1

                self.eatWhitespace(True)
                self._output.add_new_line()

                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 (self._input.lookBack("&")
                                 or self.foundNestedPseudoClass())
                        and not self._input.lookBack("(")
                        and not insideAtExtend and parenLevel == 0):
                    # 'property: value' delimiter
                    # which could be in a conditional group query
                    self.print_string(":")
                    if not insidePropertyValue:
                        insidePropertyValue = True
                        self._output.space_before_token = True
                        self.eatWhitespace(True)
                        self.indent()

                else:
                    # sass/less parent reference don't use a space
                    # sass nested pseudo-class don't use a space

                    # preserve space before pseudoclasses/pseudoelements,
                    # as it means "in any child"
                    if self._input.lookBack(" "):
                        self._output.space_before_token = True
                    if self._input.peek() == ":":
                        # pseudo-element
                        self._ch = self._input.next()
                        self.print_string("::")
                    else:
                        # pseudo-element
                        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 == ";":
                if parenLevel == 0:
                    if insidePropertyValue:
                        self.outdent()
                        insidePropertyValue = False
                    insideAtExtend = False
                    insideAtImport = False
                    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 self._input.peek() != "/":
                        self._output.add_new_line()
                else:
                    self.print_string(self._ch)
                    self.eatWhitespace(True)
                    self._output.space_before_token = True
            elif self._ch == "(":
                # may be a url
                if self._input.lookBack("url"):
                    self.print_string(self._ch)
                    self.eatWhitespace()
                    parenLevel += 1
                    self.indent()
                    self._ch = self._input.next()
                    if self._ch in {")", '"', "'"}:
                        self._input.back()
                    elif self._ch is not None:
                        self.print_string(self._ch + self.eatString(")"))
                        if parenLevel:
                            parenLevel -= 1
                            self.outdent()
                else:
                    self.preserveSingleSpace(isAfterSpace)
                    self.print_string(self._ch)
                    self.eatWhitespace()
                    parenLevel += 1
                    self.indent()
            elif self._ch == ")":
                if parenLevel:
                    parenLevel -= 1
                    self.outdent()
                self.print_string(self._ch)
            elif self._ch == ",":
                self.print_string(self._ch)
                self.eatWhitespace(True)
                if (self._options.selector_separator_newline
                        and not insidePropertyValue and parenLevel == 0
                        and not insideAtImport and not insideAtExtend):
                    self._output.add_new_line()
                else:
                    self._output.space_before_token = True
            elif ((self._ch == ">" or self._ch == "+" or self._ch == "~")
                  and not insidePropertyValue and parenLevel == 0):
                # handle combinator spacing
                if self._options.space_around_combinator:
                    self._output.space_before_token = True
                    self.print_string(self._ch)
                    self._output.space_before_token = True
                else:
                    self.print_string(self._ch)
                    self.eatWhitespace()
                    # squash extra whitespace
                    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()
                self.print_string("=")
                if bool(whitespaceChar.search(self._ch)):
                    self._ch = ""
            elif self._ch == "!" and not (self._input.lookBack("\\")):
                # !important
                self.print_string(" ")
                self.print_string(self._ch)
            else:
                self.preserveSingleSpace(isAfterSpace)
                self.print_string(self._ch)

        sweet_code = self._output.get_code(self._options.eol)

        return sweet_code
Exemple #4
0
    def beautify(self):
        if self._options.disabled:
            return self.__source_text

        source_text = self.__source_text

        if self._options.eol == 'auto':
            self._options.eol = '\n'
            if self.lineBreak.search(source_text or ''):
                self._options.eol = self.lineBreak.search(source_text).group()

        # HACK: newline parsing inconsistent. This brute force normalizes the
        # input newlines.
        source_text = re.sub(self.allLineBreaks, '\n', source_text)
        baseIndentString = re.search("^[\t ]*", source_text).group(0)

        self._output = Output(self._options, baseIndentString)

        self._input = InputScanner(source_text)

        self._indentLevel = 0
        self._nestedLevel = 0

        self._ch = None
        parenLevel = 0

        insideRule = False
        insidePropertyValue = False
        enteringConditionalGroup = False
        insideAtExtend = False
        insideAtImport = False
        topCharacter = self._ch

        while True:
            whitespace = self._input.read(whitespacePattern)
            isAfterSpace = whitespace != ''
            previous_ch = topCharacter
            self._ch = self._input.next()
            topCharacter = self._ch

            if not self._ch:
                break
            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.
                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
                self.eatWhitespace(True)

                # Block comments are followed by a new line so they don't
                # share a line with other properties
                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
                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 == '@':
                self.preserveSingleSpace(isAfterSpace)

                # deal with less propery mixins @{...}
                if self._input.peek() == '{':
                    self.print_string(self._ch + self.eatString('}'))
                else:
                    self.print_string(self._ch)
                    # strip trailing space, for hash property check
                    variableOrRule = self._input.peekUntilAfter(
                        re.compile(r"[: ,;{}()[\]\/='\"]"))

                    if variableOrRule[-1] in ": ":
                        # wwe have a variable or pseudo-class, add it and
                        # insert one space before continuing
                        variableOrRule = self.eatString(": ")
                        if variableOrRule[-1].isspace():
                            variableOrRule = variableOrRule[:-1]
                        self.print_string(variableOrRule)
                        self._output.space_before_token = True

                    if variableOrRule[-1].isspace():
                        variableOrRule = variableOrRule[:-1]

                    if variableOrRule == "extend":
                        insideAtExtend = True
                    elif variableOrRule == "import":
                        insideAtImport = True

                    # might be a nesting at-rule
                    if variableOrRule in self.NESTED_AT_RULE:
                        self._nestedLevel += 1
                        if variableOrRule in self.CONDITIONAL_GROUP_RULE:
                            enteringConditionalGroup = True
                    elif not insideRule and parenLevel == 0 and \
                            variableOrRule[-1] == ":":
                        insidePropertyValue = True
                        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
                    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 = self._indentLevel > self._nestedLevel
                else:
                    # otherwise, declarations are also allowed
                    insideRule = self._indentLevel >= self._nestedLevel

                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)
                self._output.add_new_line()
            elif self._ch == '}':
                self.outdent()
                self._output.add_new_line()
                if previous_ch == '{':
                    self._output.trim(True)
                insideAtExtend = False
                insideAtImport = False
                if insidePropertyValue:
                    self.outdent()
                    insidePropertyValue = False
                self.print_string(self._ch)
                insideRule = False
                if self._nestedLevel:
                    self._nestedLevel -= 1

                self.eatWhitespace(True)
                self._output.add_new_line()

                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 (self._input.lookBack('&') or
                             self.foundNestedPseudoClass()) and \
                        not self._input.lookBack('(') and not insideAtExtend:
                    # 'property: value' delimiter
                    # which could be in a conditional group query
                    self.print_string(":")
                    if not insidePropertyValue:
                        insidePropertyValue = True
                        self._output.space_before_token = True
                        self.eatWhitespace(True)
                        self.indent()

                else:
                    # sass/less parent reference don't use a space
                    # sass nested pseudo-class don't use a space

                    # preserve space before pseudoclasses/pseudoelements,
                    # as it means "in any child"
                    if self._input.lookBack(' '):
                        self._output.space_before_token = True
                    if self._input.peek() == ":":
                        # pseudo-element
                        self._ch = self._input.next()
                        self.print_string("::")
                    else:
                        # pseudo-element
                        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 == ';':
                if insidePropertyValue:
                    self.outdent()
                    insidePropertyValue = False
                insideAtExtend = False
                insideAtImport = False
                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 self._input.peek() is not '/':
                    self._output.add_new_line()
            elif self._ch == '(':
                # may be a url
                if self._input.lookBack("url"):
                    self.print_string(self._ch)
                    self.eatWhitespace()
                    self._ch = self._input.next()
                    if self._ch in {')', '"', '\''}:
                        self._input.back()
                        parenLevel += 1
                    elif self._ch is not None:
                        self.print_string(self._ch + self.eatString(')'))
                else:
                    parenLevel += 1
                    self.preserveSingleSpace(isAfterSpace)
                    self.print_string(self._ch)
                    self.eatWhitespace()
            elif self._ch == ')':
                self.print_string(self._ch)
                parenLevel -= 1
            elif self._ch == ',':
                self.print_string(self._ch)
                self.eatWhitespace(True)
                if self._options.selector_separator_newline and \
                        not insidePropertyValue and parenLevel < 1 and \
                        not insideAtImport:
                    self._output.add_new_line()
                else:
                    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._options.space_around_combinator:
                    self._output.space_before_token = True
                    self.print_string(self._ch)
                    self._output.space_before_token = True
                else:
                    self.print_string(self._ch)
                    self.eatWhitespace()
                    # squash extra whitespace
                    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()
                self.print_string('=')
                if bool(whitespaceChar.search(self._ch)):
                    self._ch = ''
            elif self._ch == '!':  # !important
                self.print_string(' ')
                self.print_string(self._ch)
            else:
                self.preserveSingleSpace(isAfterSpace)
                self.print_string(self._ch)

        sweet_code = self._output.get_code(self._options.eol)

        return sweet_code