From 98a428607295f33f6d31ce1e6451ee889d03335b Mon Sep 17 00:00:00 2001 From: Sam Anderson Date: Thu, 26 Jan 2012 15:56:45 -0500 Subject: [PATCH] CSS Beautifier ported so far --- .gitignore | 1 + README.md | 31 +----- WebBeautify.py | 19 ++++ lib/__init__.py | 1 + lib/cssbeautify.py | 228 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 250 insertions(+), 30 deletions(-) create mode 100644 .gitignore create mode 100644 WebBeautify.py create mode 100644 lib/__init__.py create mode 100644 lib/cssbeautify.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5d80c08d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +HTMLPrettify.pyc diff --git a/README.md b/README.md index 5873879f..35647c92 100644 --- a/README.md +++ b/README.md @@ -1,30 +1 @@ -# HTML, CSS and Javascript code formatter for Sublime Text editor via node.js -#### [Sublime Text 2](http://www.sublimetext.com/2) -#### [JSBeautifier](http://jsbeautifier.org/) -#### [Node.js download](http://nodejs.org/#download) - -## About -This is a Sublime Text 2 plugin and build system allowing you to format your HTML, CSS and JavaScript code. It uses a set of nice beautifier libs made by Einar Lielmanis and Noah Kantrowitz. The formatters are written in JavaScript, so you'll need something (node.js) to interpret JavaScript code outside the browser. - -This will work with both HTML, CSS and JavaScript files. - -## Installation -First of all, be sure you have [node.js](http://nodejs.org/#download) installed in order to run the beautifier. After you've installed node.js, you will need to setup this plugin. -Each OS has a different `Packages` folder required by Sublime Text. Open it via Preferences -> Browse Packages, and copy this repository contents to the `HTMLPrettify` folder there. - -The shorter way of doing this is: -#### Mac -`git clone git://github.com/victorporof/Sublime-HTMLPrettify.git ~/Library/Application\ Support/Sublime\ Text\ 2/Packages/HTMLPrettify` - -#### Windows -`git clone git://github.com/victorporof/Sublime-HTMLPrettify.git %APPDATA%\Sublime Text 2\Packages\HTMLPrettify` - -## Usage -Open a HTML or JavaScript file, pop out the console in Sublime Text from View -> Show Console, and type `view.run_command("htmlprettify")`. - -Writing commands in the console is ugly. Set up your own key combo for this, by going to Preferences -> Key Bindings - Default, and adding a command in that huge array: `{ "keys": ["super+shift+h"], "command": "htmlprettify" }`. You can use any other command you want, thought most of them are already taken. - -## Customize -The `HTMLPrettify.py` script has some predefined settings regarding the indentation size, indentation character, maximum chars per line and brace styling. Customize these settings by modifying the script with your desired values (see the [JSBeautifier options](https://github.com/einars/js-beautify/blob/master/beautify-html.js)). - -Have fun! \ No newline at end of file +Beginnings of a Python port of [Sublime-HTMLPrettify](https://github.com/victorporof/Sublime-HTMLPrettify). diff --git a/WebBeautify.py b/WebBeautify.py new file mode 100644 index 00000000..b220601e --- /dev/null +++ b/WebBeautify.py @@ -0,0 +1,19 @@ +import commands +import os +import re +import sublime +import sublime_plugin +from .lib import * + + +class WebBeautifyCommand(sublime_plugin.TextCommand): + + def run(self, edit): + self.save() + self.beautify(edit) + + def save(self): + self.view.run_command("save") + + def prettify(self, edit): + pass diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 00000000..e700b5f1 --- /dev/null +++ b/lib/__init__.py @@ -0,0 +1 @@ +from cssbeautify import css_beautifier diff --git a/lib/cssbeautify.py b/lib/cssbeautify.py new file mode 100644 index 00000000..b7d1b170 --- /dev/null +++ b/lib/cssbeautify.py @@ -0,0 +1,228 @@ +import re +import string + + +class CssBeautify(object): + + def __init__(self): + self.comment = False + self.depth = 0 + self.formatted = '' + self.index = 0 + self.open_brace_suffix = True + self.options = { + 'indent': '\t' + } + self.quote = None + self.state = { + 'start': 0, + 'at_rule': 1, + 'block': 2, + 'selector': 3, + 'ruleset': 4, + 'property': 5, + 'separator': 6, + 'expression': 7 + } + + def __call__(self, css, options={}, *args): + + self.index = 0 + self.formatted = '' + + for k, v in self.options.iteritems(): + if options[k]: + self.options[k] = options[k] + + length = len(css) + state = self.state.start + + # Goodbye CRLF + re.sub(r'\r\n', '\n', css) + + while self.index < length: + ch = css[self.index] + ch2 = css[self.index + 1] + self.index += 1 + + # Inside a string literal? + if self.is_quote(self.quote): + self.formatted += ch + if ch == self.quote: + quote = None + if ch == '\\' and ch2 == quote: + # Don't treat escaped character as the closing quote + self.formatted += ch2 + self.index += 1 + continue + + # Starting a string literal? + if self.is_quote(ch): + self.formatted += ch + self.quote = ch + continue + + if self.comment: + self.formatted += ch + if ch == '*' and ch2 == '/': + self.comment = False + self.formatted += ch2 + self.index += 1 + continue + else: + if ch == '/' and ch2 == '*': + self.comment = True + self.formatted += ch + self.formatted += ch2 + self.index += 1 + continue + + if state == self.state.start: + + # Copy white spaces and control characters + if ch <= ' ' or ord(ch) >= 128: + state = self.state.start + self.formatted += ch + continue + + # Selector or at_rule + if self.is_name(ch) or ch == '@': + + # Clear trailing whitespaces and linefeeds + self.formatted.rstrip() + + # After finishing a ruleset or directive statement, + # there should be one blank line + if (str[-1] == '}') or str[-1] == ';': + self.formatted = str + '\n\n' + else: + # After block comment, keep all the linfeeds but start + # from the first column (remove whitespaces prefix). + while True: + ch2 = self.formatted[-1] + if ch2 != ' ' and ord(ch2) != 9: + break + self.formatted = self.formatted[0:-1] + + self.formatted += ch + state = self.state.at_rule if ch == '@' else self.state.selector + continue + + if state == self.state.at_rule: + + # ; terminates a statement + if ch == ';': + self.formatted += ch + state = self.state.start + continue + + # '{' starts a block + if ch == '{': + self.open_block() + state = self.state.block + continue + + self.formatted += ch + continue + + if state == self.state.block: + if self.is_name(ch): + + # Clear trailing whitespace and linefeeds + str = self.formatted.rstrip() + + # Insert blank line if necessary + if str[-1] == '}': + self.formatted = str + '\n\n' + else: + while True: + ch2 = self.formatted[-1] + if ch2 != ' ' and ord(ch2) != 9: + break + self.formatted = self.formatted[0:-1] + + self.append_indent() + self.formatted += ch + state = self.state.selector + continue + + if ch == '}': + self.close_block() + state = self.state.start + continue + + self.formatted += ch + continue + + if state = self.state.selector: + + # '{' starts the ruleset + if ch == '{': + self.open_block() + state = self.state.ruleset + continue + + # '}' resets the state + if ch == '}': + self.close_block() + state = self.state.start + continue + + self.formatted += ch + continue + + if state == self.state.ruleset: + if ch == '}': + self.close_block() + state = self.state.start + if self.depth > 0: + state = self.state.block + continue + + if ch == '\n': + self.formatted += '\n' + continue + + if not self.is_whitespace(ch): + self.formatted += '\n' + self.append_indent + + self.formatted += ch + + def is_name(self, c): + return ch in string.letters or + ch in string.digits or + ch in '-_*.:#' + + def is_whitespace(self, c): + return c in string.whitespace + + def is_quote(self, c): + return c in '\'"' + + def append_indent(self): + self.formatted += (self.options.indent * self.depth) + + def open_block(self): + self.formatted = self.formatted.rstrip() + + if self.open_brace_suffix: + self.formatted += ' {' + else: + self.formatted += '\n' + self.append_indent + self.formatted += '{' + + if ch2 != '\n': + self.formatted += '\n' + + self.depth += 1 + + def close_block(self): + self.depth -= 1 + self.formatted = self.formatted.rstrip() + self.formatted += '\n' + self.append_indent() + self.formatted += '}' + +css_beautifier = CssBeautify()