Skip to content

Instantly share code, notes, and snippets.

@hudochenkov
Last active August 29, 2015 14:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hudochenkov/d0271b9ded49f5dac1a4 to your computer and use it in GitHub Desktop.
Save hudochenkov/d0271b9ded49f5dac1a4 to your computer and use it in GitHub Desktop.
import sublime
import sublime_plugin
import re
import os
from html.parser import HTMLParser
class EcsstractorCommand(sublime_plugin.WindowCommand):
def run(self):
view = self.window.active_view()
plugin_settings = sublime.load_settings('eCSStractor.sublime-settings')
indentation = plugin_settings.get('indentation', '\t')
bem_nesting = plugin_settings.get('bem_nesting')
syntax = 'Packages/CSS/CSS.tmLanguage'
# if view have any selection then work with selection, else with whole view
selection = view.sel()[0]
if len(selection) > 0:
for sel in view.sel():
region = sublime.Region(
view.line(min(sel.a, sel.b)).a, # line start of first line
view.line(max(sel.a, sel.b)).b # line end of last line
)
else:
region = sublime.Region(0, view.size())
source = view.substr(region)
if bem_nesting:
output = self.generateBEM(source)
# set sass syntax if proper package is installed
scss_syntax = os.path.join(sublime.packages_path(), 'Syntax Highlighting for Sass', 'Syntaxes', 'SCSS.tmLanguage')
if os.path.exists(scss_syntax):
syntax = 'Packages/Syntax Highlighting for Sass/Syntaxes/SCSS.tmLanguage'
else:
output = self.generateOutput(source)
# if output not empty
if output:
# create new view
new_file = self.window.new_file()
new_file.set_syntax_file(syntax)
new_file.run_command("ecsstractor_insert", {"text": output})
else:
sublime.status_message("eCSStractor can't find any classes")
def generateOutput(self, source = ""):
css_template = "selector {\n}"
output = ""
# parsing
parsed = parser()
parsed.feed(source)
# format output
for i in range(len(parsed.classes)):
output += css_template.replace("selector", "." + parsed.classes[i]) + "\n"
return output
def generateBEM(self, source = ""):
plugin_settings = sublime.load_settings('eCSStractor.sublime-settings')
indentation = plugin_settings.get('indentation', '\t')
element_separator = plugin_settings.get('bem.element_separator', '__')
modifier_separator = plugin_settings.get('bem.modifier_separator', '--')
parent_symbol = plugin_settings.get('preprocessor.parent_symbol', '&')
output = ""
selectors = []
# parsing
parsed = parser()
parsed.feed(source)
# build tree
for i in range(len(parsed.classes)):
selector = parsed.classes[i]
block = {}
element = {}
# if block has element
if element_separator in selector:
parts = selector.split(element_separator)
# check if block with this name exist already
hasBlock = self.hasChild(selectors, "name", parts[0])
# if block is exist link to list
if hasBlock is not False:
block = selectors[hasBlock]
# if block is not exist give it name
if hasBlock is False:
block["name"] = parts[0]
# if elements list exist in block
if "elements" not in block:
block["elements"] = []
# get element and his modifier
elementParts = parts[1].split(modifier_separator)
# check if element with this name exist in block already
hasElement = self.hasChild(block["elements"], "name", elementParts[0])
# if element is exist link to list
if hasElement is not False:
element = block["elements"][hasElement]
# if element is not exist give it name
if hasElement is False:
element["name"] = elementParts[0]
# if element has modifier
if len(elementParts) > 1:
# if modifiers list exist in element
if "modifiers" not in element:
element["modifiers"] = []
# add modifier
element["modifiers"].append(elementParts[1])
# if it is new element add it to block
if hasElement is False:
block["elements"].append(element)
# if it is new block add it to list
if hasBlock is False:
selectors.append(block)
# if block has modifier
elif modifier_separator in selector:
parts = selector.split(modifier_separator)
hasBlock = self.hasChild(selectors, "name", parts[0])
if hasBlock is not False:
block = selectors[hasBlock]
if hasBlock is False:
block["name"] = parts[0]
if "modifiers" not in block:
block["modifiers"] = []
block["modifiers"].append(parts[1])
if hasBlock is False:
selectors.append(block)
else:
hasBlock = self.hasChild(selectors, "name", selector)
if hasBlock is False:
block["name"] = selector
selectors.append(block)
# format output
for block in selectors:
output += "." + block["name"] + " {\n"
indent = indentation
indent1 = indent * 1
indent2 = indent * 2
if "modifiers" in block:
for modifier in block["modifiers"]:
output += indent1 + parent_symbol + modifier_separator + modifier + " {\n"
output += indent1 + "}\n"
if "elements" in block:
for element in block["elements"]:
output += indent1 + parent_symbol + element_separator + element["name"] + " {\n"
if "modifiers" in element:
for modifier in element["modifiers"]:
output += indent2 + parent_symbol + modifier_separator + modifier + " {\n"
output += indent2 + "}\n"
output += indent1 + "}\n"
output += "}\n"
return output
# check existance of key_name:key in listo
def hasChild(self, listo, key_name, key):
for y in range(len(listo)):
if listo[y][key_name] == key:
return y
return False
class EcsstractorInsertCommand(sublime_plugin.TextCommand):
def run(self, edit, text):
self.view.insert(edit, 0, text)
class parser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.classes = []
def handle_starttag(self, tag, attrs):
for name, value in attrs:
if name == "class":
# remove whitespaces before and after string
value = value.strip();
# if class="" not empty
if len(value) > 0:
# split class string by whitespaces, in case multiple classes presented
elementClasses = re.split("\s+", value)
for i in range(len(elementClasses)):
# add class to list if it's already not there
if elementClasses[i] not in self.classes:
self.classes.append(elementClasses[i])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment