├── .editorconfig ├── .gitignore ├── LICENSE-MIT ├── Main.sublime-menu ├── README.md ├── SassBeautify.py ├── SassBeautify.sublime-commands ├── SassBeautify.sublime-settings ├── assets ├── example.sass ├── example.scss └── screenshot.png ├── messages.json ├── messages └── install.txt └── tests └── test.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.py] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.json] 16 | indent_style = tab 17 | indent_size = 4 18 | 19 | [*.sublime-{commands,settings,menu}] 20 | indent_style = tab 21 | indent_size = 4 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Richard Willis 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Preferences", 4 | "mnemonic": "n", 5 | "id": "preferences", 6 | "children": 7 | [ 8 | { 9 | "caption": "Package Settings", 10 | "mnemonic": "P", 11 | "id": "package-settings", 12 | "children": 13 | [ 14 | { 15 | "caption": "SassBeautify", 16 | "children": 17 | [ 18 | { 19 | "command": "open_file", 20 | "args": {"file": "${packages}/SassBeautify/SassBeautify.sublime-settings"}, 21 | "caption": "Settings – Default" 22 | }, 23 | { 24 | "command": "open_file", 25 | "args": {"file": "${packages}/User/SassBeautify.sublime-settings"}, 26 | "caption": "Settings – User" 27 | }, 28 | { "caption": "-" } 29 | ] 30 | } 31 | ] 32 | } 33 | ] 34 | } 35 | ] 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SassBeautify 2 | 3 | https://sublime.wbond.net/packages/SassBeautify 4 | 5 | A Sublime Text plugin that beautifies Sass files. (Compatible with Sublime Text 2 & 3.) 6 | 7 | ![ScreenShot](https://raw.github.com/badsyntax/SassBeautify/master/assets/screenshot.png) 8 | 9 | ## Dependencies 10 | 11 | This plugin uses `sass-convert`, and so you need to have sass installed. Read the [sass download page](http://sass-lang.com/download.html) to view the installation options. 12 | 13 | It's a good idea to always use the latest version of Sass. 14 | 15 | ## Installation 16 | 17 | **Option 1 (recommended)** 18 | 19 | Install via package control: 20 | 21 | 1. Ensure you have package control installed, see here: https://sublime.wbond.net/installation 22 | 2. Install the package: open up the command palette (ctrl/cmd + shift + p), execute the following command: 23 | 'Package Control: Install Package', then enter 'SassBeautify' 24 | 25 | **Option 2** 26 | 27 | Manual download: 28 | 29 | 1. Download the zip file file here: https://github.com/badsyntax/SassBeautify/archive/master.zip 30 | 2. Unzip the archive, rename the 'SassBeautify-master' folder to 'SassBeautify' and move it into your Sublime Text 'Packages' directory. 31 | 32 | ## Usage 33 | 34 | **Default usage** 35 | 36 | Run the plugin from the command palette: 37 | 38 | 1. Open the command palette (ctrl/cmd + shift + p) 39 | 2. Enter 'SassBeautify' 40 | 41 | **Conversion usage** 42 | 43 | Run the conversion commands from the command palette: 44 | 45 | 1. Open the command palette (ctrl/cmd + shift + p) 46 | 2. Type 'SassBeautify' 47 | 3. Choose one of the following options: 48 | * Convert from CSS to current type 49 | * Convert from SCSS to current type 50 | * Convert from SASS to current type 51 | 52 | ## Settings 53 | 54 | Once installed, you can customize how the beautification works by changing the package settings. 55 | 56 | 1. Open the default settings from the preferences menu: `Preferences >> Package Settings >> SassBeautify >> Settings - Default` 57 | 2. Copy the settings and paste them into your user settings file: `Preferences >> Package Settings >> SassBeautify >> Settings - User` 58 | 3. Change the user settings. 59 | 60 | ### Settings overview 61 | 62 | The following settings can be adjusted: 63 | 64 | ```javascript 65 | { 66 | // How many spaces to use for each level of indentation. "t" means use hard tabs. 67 | "indent": 4, 68 | // Convert underscores to dashes. 69 | "dasherize": false, 70 | // Output the old-style ":prop val" property syntax. Only meaningful when generating Sass. 71 | "old": false, 72 | // Custom environment PATH. 73 | "path": false, 74 | // Custom environment GEM_PATH. 75 | "gemPath": false, 76 | // Beautify the sass file after saving it? 77 | "beautifyOnSave": false, 78 | // Keep "inline" comments inline? 79 | "inlineComments": false, 80 | // Add a new line between selectors? 81 | "newlineBetweenSelectors": false, 82 | // Use single quotes everywhere 83 | "useSingleQuotes": false 84 | } 85 | ``` 86 | 87 | ### Key bindings 88 | 89 | The plugin does not set any default key bindings, thus you will need to specify your own. 90 | 91 | In your keymap file (Preferences >> Key bindings - User), add a custom key binding: 92 | 93 | ```json 94 | [ 95 | { 96 | "keys": ["alt+w"], 97 | "command": "sass_beautify" 98 | } 99 | ] 100 | ``` 101 | 102 | 103 | ## Issues with ruby, Sass and your PATH 104 | 105 | If you installed ruby and sass via a version manager tool like [RVM](https://rvm.io/), [rbenv](https://github.com/sstephenson/rbenv) or via an installer like [ruby installer](http://rubyinstaller.org/), then you're likely to encounter issues with running `sass-convert` from Sublime Text. 106 | 107 | ### Compatibility with RVM/rbenv 108 | 109 | You need to specify the custom `PATH` and `GEM_PATH `values in your SassBeautify user settings. 110 | 111 | Follow the steps below: 112 | 113 | 1. Open up terminal 114 | 2. Run: `echo $PATH` 115 | 3. Copy the *entire* `PATH` into the 'path' setting 116 | 4. Run: `echo $GEM_PATH` 117 | 5. Copy the *entire* `GEM_PATH` into the 'gemPath' setting 118 | 119 | ### Compatibility with RubyInstaller 120 | 121 | During the install process, there should be an option to add ruby to your environment PATH. Ensure this option is selected. 122 | 123 | ## Issues 124 | 125 | This plugin should work on Linux (tested on Ubuntu 12.04), Windows (tested on Windows 7/8) and OSX (tested on 10.5.7). 126 | 127 | Please [create an issue](https://github.com/badsyntax/SassBeautify/issues) if you find it doesn't work as expected on your setup. 128 | 129 | ## Thanks 130 | 131 | Thanks to the [contributors](https://github.com/badsyntax/SassBeautify/graphs/contributors) and to all the people 132 | who have tested and reported issues. 133 | 134 | ## License 135 | 136 | Licensed under the MIT license. Created by [Richard Willis](http://badsyntax.co/) 137 | -------------------------------------------------------------------------------- /SassBeautify.py: -------------------------------------------------------------------------------- 1 | ''' 2 | SassBeautify - A Sublime Text 2/3 plugin that beautifies sass files. 3 | https://github.com/badsyntax/SassBeautify 4 | ''' 5 | import sublime 6 | import sublime_plugin 7 | import os 8 | import subprocess 9 | import threading 10 | import re 11 | 12 | __version__ = '1.4.1' 13 | __author__ = 'Richard Willis' 14 | __email__ = 'willis.rh@gmail.com' 15 | __copyright__ = 'Copyright 2013, Richard Willis' 16 | __license__ = 'MIT' 17 | __credits__ = ['scotthovestadt', 'WilliamVercken', 'Napanee'] 18 | 19 | 20 | class ExecSassCommand(threading.Thread): 21 | 22 | ''' 23 | This is a threaded class that we use for running the sass command in a 24 | different thread. We thread the sub-process se we don't lockup the UI. 25 | ''' 26 | 27 | def __init__(self, cmd, env, stdin): 28 | 29 | self.cmd = cmd 30 | self.env = env 31 | self.stdin = stdin 32 | self.returncode = 0 33 | self.stdout = None 34 | self.stderr = None 35 | 36 | threading.Thread.__init__(self) 37 | 38 | def run(self): 39 | ''' 40 | Executes the command in a sub-process. 41 | ''' 42 | try: 43 | process = subprocess.Popen( 44 | self.cmd, 45 | env=self.env, 46 | shell=sublime.platform() == 'windows', 47 | stdin=subprocess.PIPE, 48 | stdout=subprocess.PIPE, 49 | stderr=subprocess.PIPE 50 | ) 51 | (self.stdout, self.stderr) = process.communicate(input=self.stdin) 52 | self.returncode = process.returncode 53 | except OSError as e: 54 | self.stderr = str(e) 55 | self.returncode = 1 56 | 57 | class SassBeautifyReplaceTextCommand(sublime_plugin.TextCommand): 58 | 59 | ''' 60 | A Text Command to replace the entire view with new text. 61 | ''' 62 | 63 | def run(self, edit, text=None): 64 | self.view.replace(edit, sublime.Region(0, self.view.size()), text) 65 | 66 | 67 | class SassBeautifyEvents(sublime_plugin.EventListener): 68 | 69 | ''' 70 | An EventListener class to utilize Sublime's events. 71 | ''' 72 | 73 | def on_post_save(self, view): 74 | ''' 75 | After saving the file, optionally beautify it. This is done 76 | on_post_save because the beautification is asynchronous. 77 | ''' 78 | settings = sublime.load_settings('SassBeautify.sublime-settings') 79 | beautify_on_save = settings.get('beautifyOnSave', False) 80 | 81 | if not SassBeautifyCommand.saving and beautify_on_save: 82 | view.run_command('sass_beautify', { 83 | 'show_errors': False 84 | }) 85 | 86 | 87 | class SassBeautifyCommand(sublime_plugin.TextCommand): 88 | 89 | ''' 90 | Our main SassBeautify Text Command. 91 | ''' 92 | 93 | saving = False 94 | viewport_pos = None 95 | selection = None 96 | 97 | def run(self, edit, action='beautify', convert_from_type=None, show_errors=True): 98 | 99 | self.action = action 100 | self.convert_from_type = convert_from_type 101 | self.settings = sublime.load_settings('SassBeautify.sublime-settings') 102 | self.show_errors = show_errors 103 | 104 | if self.check_file(): 105 | self.beautify() 106 | 107 | def check_file(self): 108 | ''' 109 | Performs some validation checks on the file to ensure we're working with 110 | something that we can beautify. 111 | ''' 112 | # A file has to be saved so we can get the conversion type from the 113 | # file extension. 114 | if self.view.file_name() is None: 115 | self.error_message('Please save this file before trying to beautify.') 116 | return False 117 | 118 | # Check the file has the correct extension before beautifying. 119 | if self.get_type() not in ['sass', 'scss']: 120 | self.error_message('Not a valid Sass file.') 121 | return False 122 | 123 | return True 124 | 125 | def error_message(self, message): 126 | ''' 127 | Shows a Sublime error message. 128 | ''' 129 | if self.show_errors: 130 | sublime.error_message(message) 131 | 132 | def beautify(self): 133 | ''' 134 | Runs the sass beautify command. 135 | ''' 136 | thread = ExecSassCommand( 137 | self.get_cmd(), 138 | self.get_env(), 139 | self.get_text() 140 | ) 141 | thread.start() 142 | self.check_thread(thread) 143 | 144 | def restore_end_of_line_comments(self, content): 145 | def restore(m): 146 | return ' ' + m.group(2) + m.group(4); 147 | 148 | # Restore line and block comments at the end of lines that have been pushed to the next line by sass-convert 149 | content = re.sub('(\s+)(//|/\\*)(---end-of-line-comment---)(.*)', restore, content) 150 | 151 | # Cleanup, some // and /* might have gotten ---end-of-line-comment--- added which were not 152 | # matched and removed by regexp (for instance, // appeared inside a block comment), 153 | # in which case we want to restore the comment. We don't need regexp, so we're using simple string replace. 154 | content = content.replace('//---end-of-line-comment---', '//') 155 | content = content.replace('/*---end-of-line-comment---', '/*') 156 | 157 | return content 158 | 159 | def beautify_newlines(self, content): 160 | def insert_newline_between_capturing_parentheses(m): 161 | return m.group(1) + '\n' + m.group(2) 162 | 163 | # Insert newline after "}" or ";" if the line after defines (or starts to define) a selector 164 | # (i.e. contains some characters followed by a "{" or "," on the same line). 165 | # This is in order to make the selector more visible and increase readability 166 | content = re.sub(re.compile('(;.*|}.*)(\n.+[{,])$', re.MULTILINE), insert_newline_between_capturing_parentheses, content) 167 | 168 | # Similar to above, except the next line starts a comment block followed by a selector 169 | matchCommentBlockRegEx = '/\\*(\\*(?!/)|[^\\*])*\\*/' 170 | content = re.sub(re.compile('(;.*|}.*)(\n +' + matchCommentBlockRegEx + '\n.+[{,])$', re.MULTILINE), insert_newline_between_capturing_parentheses, content) 171 | 172 | # Similar to above, except the next line is a commented out line followed by a selector 173 | content = re.sub(re.compile('(;.*|}.*)(\n +//.*\n.+[{,])$', re.MULTILINE), insert_newline_between_capturing_parentheses, content) 174 | 175 | return content 176 | 177 | def use_single_quotes(self, content): 178 | content = content.replace('"', '\'') 179 | return content 180 | 181 | def check_thread(self, thread, i=0, dir=1): 182 | ''' 183 | Checks if the thread is still running. 184 | ''' 185 | 186 | # This animates a little activity indicator in the status area 187 | # Taken from https://github.com/wbond/sublime_prefixr 188 | before = i % 8 189 | after = (7) - before 190 | if not after: 191 | dir = -1 192 | if not before: 193 | dir = 1 194 | i += dir 195 | 196 | self.view.set_status( 197 | 'sassbeautify', 198 | 'SassBeautify [%s=%s]' % (' ' * before, ' ' * after) 199 | ) 200 | 201 | if thread.is_alive(): 202 | return sublime.set_timeout(lambda: self.check_thread(thread, i, dir), 100) 203 | 204 | self.view.erase_status('sassbeautify') 205 | self.handle_process(thread.returncode, thread.stdout, thread.stderr) 206 | 207 | def handle_process(self, returncode, output, error): 208 | ''' 209 | Handles the streams from the threaded process. 210 | ''' 211 | 212 | if type(output) is bytes: 213 | output = output.decode('utf-8') 214 | 215 | if type(error) is bytes: 216 | error = error.decode('utf-8') 217 | 218 | if returncode != 0: 219 | return self.error_message( 220 | 'There was an error beautifying your Sass:\n\n' + error 221 | ) 222 | 223 | # Ensure we're working with unix-style newlines. 224 | # Fixes issue on windows with Sass < v3.2.10. 225 | output = '\n'.join(output.splitlines()) 226 | 227 | if self.settings.get('inlineComments', False): 228 | output = self.restore_end_of_line_comments(output) 229 | 230 | if self.settings.get('newlineBetweenSelectors', False): 231 | output = self.beautify_newlines(output) 232 | 233 | if self.settings.get('useSingleQuotes', False): 234 | output = self.use_single_quotes(output) 235 | 236 | self.viewport_pos = self.view.viewport_position() 237 | self.selection = self.view.sel()[0] 238 | 239 | # Update the text in the editor 240 | self.view.run_command('sass_beautify_replace_text', {'text': output}) 241 | 242 | # Save the file 243 | sublime.set_timeout(self.save, 1) 244 | 245 | def get_cmd(self): 246 | ''' 247 | Generates the sass command. 248 | ''' 249 | filetype = self.get_type() 250 | 251 | cmd = [ 252 | 'sass-convert', 253 | '--unix-newlines', 254 | '--stdin', 255 | '--indent', str(self.settings.get('indent', 't')), 256 | '--from', filetype if self.action == 'beautify' else self.convert_from_type, 257 | '--to', filetype 258 | ] 259 | 260 | # Convert underscores to dashes. 261 | if self.settings.get('dasherize', False): 262 | cmd.append('--dasherize') 263 | 264 | # Output the old-style ':prop val' property syntax. 265 | # Only meaningful when generating Sass. 266 | if self.settings.get('old', False) and filetype == 'sass': 267 | cmd.append('--old') 268 | 269 | return cmd 270 | 271 | def get_env(self): 272 | ''' 273 | Generates the process environment. 274 | ''' 275 | env = os.environ.copy() 276 | 277 | if self.settings.get('path', False): 278 | env['PATH'] = self.settings.get('path') 279 | 280 | if self.settings.get('gemPath', False): 281 | env['GEM_PATH'] = self.settings.get('gemPath') 282 | 283 | return env 284 | 285 | def get_ext(self): 286 | ''' 287 | Extracts the extension from the filename. 288 | ''' 289 | (basename, ext) = os.path.splitext(self.view.file_name()) 290 | return ext.strip('.') 291 | 292 | def get_type(self): 293 | ''' 294 | Returns the file type. 295 | ''' 296 | filetype = self.get_ext() 297 | # Added experimental CSS support with issue #27. 298 | # If this is a CSS file, then we're to treat it exactly like a SCSS file. 299 | if self.action == 'beautify' and filetype == 'css': 300 | filetype = 'scss' 301 | return filetype 302 | 303 | def get_text(self): 304 | 305 | def mark_end_of_line_comment(m): 306 | return m.group(1) + m.group(2) + '---end-of-line-comment---' + m.group(3) 307 | 308 | ''' 309 | Gets the sass text from the Sublime view. 310 | ''' 311 | content = self.view.substr(sublime.Region(0, self.view.size())) 312 | 313 | if self.settings.get('inlineComments', False): 314 | ''' 315 | Mark comments at the end of lines so we can move them back to the end of the line after sass-convert has pushed them to a new line 316 | ''' 317 | content = re.sub(re.compile('([;{}]+[ \t]*)(//|/\\*)(.*)$', re.MULTILINE), mark_end_of_line_comment, content) 318 | 319 | return content.encode('utf-8') 320 | 321 | def save(self): 322 | ''' 323 | Saves the file and show a success message. 324 | ''' 325 | 326 | # We have to store the state to prevent us getting in an infinite loop 327 | # when beautifying on_post_save. 328 | SassBeautifyCommand.saving = True 329 | self.view.run_command('save') 330 | SassBeautifyCommand.saving = False 331 | 332 | self.view.set_viewport_position(self.viewport_pos, False) 333 | self.view.sel().clear() 334 | self.view.sel().add(self.selection) 335 | 336 | sublime.status_message('Successfully beautified ' + self.view.file_name()) 337 | -------------------------------------------------------------------------------- /SassBeautify.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "SassBeautify", 4 | "command": "sass_beautify", 5 | "args": { 6 | "action": "beautify" 7 | } 8 | }, 9 | { 10 | "caption": "SassBeautify: Convert from CSS to current type", 11 | "command": "sass_beautify", 12 | "args": { 13 | "action": "convert", 14 | "convert_from_type": "css" 15 | } 16 | }, 17 | { 18 | "caption": "SassBeautify: Convert from SCSS to current type", 19 | "command": "sass_beautify", 20 | "args": { 21 | "action": "convert", 22 | "convert_from_type": "scss" 23 | } 24 | }, 25 | { 26 | "caption": "SassBeautify: Convert from SASS to current type", 27 | "command": "sass_beautify", 28 | "args": { 29 | "action": "convert", 30 | "convert_from_type": "sass" 31 | } 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /SassBeautify.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "indent": 4, 3 | "dasherize": false, 4 | "old": false, 5 | "path": false, 6 | "beautifyOnSave": false, 7 | "inlineComments": false, 8 | "newlineBetweenSelectors": false 9 | } 10 | -------------------------------------------------------------------------------- /assets/example.sass: -------------------------------------------------------------------------------- 1 | /** 2 | * example.sass 3 | * An example sass file with horrid formatting. 4 | * Use the SassBeautify plugin to fix the formatting of this file. 5 | 6 | $blue: #3bbfce 7 | $margin: 16px 8 | 9 | .content-navigation 10 | border-color : $blue 11 | color: darken( $blue, 9%) 12 | 13 | .border 14 | 15 | 16 | 17 | padding: $margin / 2 18 | margin: $margin / 2 19 | 20 | 21 | 22 | 23 | // Here is an inline comment describing the border-color property 24 | border-color: $blue -------------------------------------------------------------------------------- /assets/example.scss: -------------------------------------------------------------------------------- 1 | 2 | @charset "UTF-8"; 3 | 4 | /** 5 | * example.scss 6 | * An example scss file with horrid formatting. 7 | * Use the SassBeautify plugin to fix the formatting of this file. 8 | */ 9 | 10 | $blue:#3bbfce;$margin: 11 | 16px; 12 | .content-navigation { 13 | // à é ç è ù 14 | border-color:$blue; 15 | color: 16 | darken($blue, 9%); 17 | } 18 | .border { 19 | padding: $margin / 2;margin:$margin / 2; // Here is an inline comment describing the border-color property 20 | border-color: 21 | 22 | 23 | 24 | $blue; 25 | .nested-selector {color: blue; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badsyntax/SassBeautify/38b6fd8e74c1a810c1b877adff938bec0bd3fa09/assets/screenshot.png -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "install": "messages/install.txt" 3 | } -------------------------------------------------------------------------------- /messages/install.txt: -------------------------------------------------------------------------------- 1 | The SassBeautify package has been successfully installed. 2 | 3 | Run the plugin from the command palette: 4 | 5 | 1. Open the command palette (ctrl + shift + p) 6 | 2. Enter 'SassBeautify' 7 | 8 | Please report issues here: https://github.com/badsyntax/SassBeautify/issues 9 | 10 | Created by Richard Willis (badsyntax.co) -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | # To run the tests: 2 | # 1. Install Sublime Text plugin: https://sublime.wbond.net/packages/UnitTesting 3 | # 2. Open the Command Palette and run "UnitTesting: Run any project test suite" 4 | # 3. Enter "SassBeautify" 5 | 6 | import sublime, sys, textwrap 7 | from unittest import TestCase 8 | 9 | # st2 10 | if sublime.version() < '3000': 11 | SassBeautifyCommand = sys.modules["SassBeautify"].SassBeautifyCommand; 12 | # st3 13 | else: 14 | SassBeautifyCommand = sys.modules["SassBeautify.SassBeautify"].SassBeautifyCommand; 15 | 16 | SassBeautifyCommandInstance = SassBeautifyCommand(None); 17 | 18 | 19 | class test_internal_function_restore_end_of_line_comments(TestCase): 20 | 21 | # Check that inline comments starting with ---end-of-line-comment--- 22 | # (which are inserted to "mark" those comments before running sass-convert) 23 | # are moved back to the previous line and restored to the original comment 24 | def test_inline_comment(self): 25 | beautified = SassBeautifyCommandInstance.restore_end_of_line_comments(textwrap.dedent("""\ 26 | 27 | h1 {} 28 | //---end-of-line-comment--- test 29 | 30 | """)) 31 | 32 | self.assertEqual(beautified, textwrap.dedent("""\ 33 | 34 | h1 {} // test 35 | 36 | """)) 37 | 38 | # Check that block comments are moved back and restored as well 39 | def test_block_comment(self): 40 | beautified = SassBeautifyCommandInstance.restore_end_of_line_comments(textwrap.dedent("""\ 41 | 42 | h1 {} 43 | /*---end-of-line-comment--- line 1 44 | line 2 */ 45 | 46 | """)) 47 | 48 | self.assertEqual(beautified, textwrap.dedent("""\ 49 | 50 | h1 {} /* line 1 51 | line 2 */ 52 | 53 | """)) 54 | 55 | 56 | class test_internal_function_beautify_newlines(TestCase): 57 | 58 | # Check that a newline is inserted between two selectors 59 | def test_insert_newline_1(self): 60 | beautified = SassBeautifyCommandInstance.beautify_newlines(textwrap.dedent("""\ 61 | 62 | .ClassA { 63 | color: red; 64 | } 65 | .ClassB { 66 | color: blue; 67 | } 68 | 69 | """)) 70 | 71 | self.assertEqual(beautified, textwrap.dedent("""\ 72 | 73 | .ClassA { 74 | color: red; 75 | } 76 | 77 | .ClassB { 78 | color: blue; 79 | } 80 | 81 | """)) 82 | 83 | # Check that a property followed by a selector is separated with a newline 84 | def test_insert_newline_2(self): 85 | beautified = SassBeautifyCommandInstance.beautify_newlines(textwrap.dedent("""\ 86 | 87 | .ClassA { 88 | color: red; 89 | .ClassB { 90 | color: blue; 91 | } 92 | } 93 | 94 | """)) 95 | 96 | self.assertEqual(beautified, textwrap.dedent("""\ 97 | 98 | .ClassA { 99 | color: red; 100 | 101 | .ClassB { 102 | color: blue; 103 | } 104 | } 105 | 106 | """)) 107 | 108 | # Check that a propery followed by inline comment and then selector is separated with newline 109 | def test_insert_newline_3(self): 110 | beautified = SassBeautifyCommandInstance.beautify_newlines(textwrap.dedent("""\ 111 | 112 | .ClassA { 113 | color: red; 114 | // This is class b 115 | .ClassB { 116 | color: blue; 117 | } 118 | } 119 | 120 | """)) 121 | 122 | self.assertEqual(beautified, textwrap.dedent("""\ 123 | 124 | .ClassA { 125 | color: red; 126 | 127 | // This is class b 128 | .ClassB { 129 | color: blue; 130 | } 131 | } 132 | 133 | """)) 134 | 135 | # Check that propery followed by inline comment and then selector is separated with newline 136 | def test_insert_newline_4(self): 137 | beautified = SassBeautifyCommandInstance.beautify_newlines(textwrap.dedent("""\ 138 | 139 | .ClassA { 140 | color: red; 141 | /* 142 | This is class b 143 | */ 144 | .ClassB { 145 | color: blue; 146 | } 147 | } 148 | 149 | """)) 150 | 151 | self.assertEqual(beautified, textwrap.dedent("""\ 152 | 153 | .ClassA { 154 | color: red; 155 | 156 | /* 157 | This is class b 158 | */ 159 | .ClassB { 160 | color: blue; 161 | } 162 | } 163 | 164 | """)) 165 | 166 | 167 | # Check that two selectors already separated by a newline doesn't get an additional newline 168 | def test_skip_insert_newline_1(self): 169 | beautified = SassBeautifyCommandInstance.beautify_newlines(textwrap.dedent("""\ 170 | 171 | .ClassA { 172 | color: red; 173 | } 174 | 175 | .ClassB { 176 | color: blue; 177 | } 178 | 179 | """)) 180 | 181 | self.assertEqual(beautified, textwrap.dedent("""\ 182 | 183 | .ClassA { 184 | color: red; 185 | } 186 | 187 | .ClassB { 188 | color: blue; 189 | } 190 | 191 | """)) 192 | 193 | # Check that two nested selectors doesn't get any newlines 194 | def test_skip_insert_newline_2(self): 195 | beautified = SassBeautifyCommandInstance.beautify_newlines(textwrap.dedent("""\ 196 | 197 | .ClassA { 198 | .ClassB { 199 | color: blue; 200 | } 201 | } 202 | 203 | """)) 204 | 205 | self.assertEqual(beautified, textwrap.dedent("""\ 206 | 207 | .ClassA { 208 | .ClassB { 209 | color: blue; 210 | } 211 | } 212 | 213 | """)) 214 | --------------------------------------------------------------------------------