sublime-less2css icon indicating copy to clipboard operation
sublime-less2css copied to clipboard

please add setting

Open mmmDanone opened this issue 8 years ago • 1 comments

please add setting for less-plugin-group-css-media-queries Thank)))

mmmDanone avatar Aug 03 '17 20:08 mmmDanone

@`# -- coding: utf-8 -- from future import print_function import os import re import sys import sublime import itertools import subprocess

IS_WINDOWS = True if sys.platform == 'win32' else False

SETTING_AUTOCOMPILE = "autoCompile" SETTING_LESSBASEDIR = "lessBaseDir" SETTING_IGNOREPREFIXEDFILES = "ignorePrefixedFiles" SETTING_LESSCCOMMAND = "lesscCommand" SETTING_MAINFILE = "main_file" SETTING_MEDIA_GROUP = "media_group" SETTING_MINIFY = "minify" SETTING_MINNAME = "minName" SETTING_OUTPUTDIR = "outputDir" SETTING_OUTPUTFILE = "outputFile" SETTING_CREATECSSSOURCEMAPS = "createCssSourceMaps" SETTING_AUTOPREFIX = "autoprefix" SETTING_DISABLEVERBOSE = 'disableVerbose' SETTING_SILENT = 'silent'

class Compiler: def init(self, view): self.view = view

    # get global settings and project settings
    settings = sublime.load_settings('less2css.sublime-settings')
    project_settings = sublime.active_window().active_view().settings().get(
    'less2css') or {}

    # Build a dictionary of settings using both system and
    # project setting files
    self.settings = {
        'auto_compile': project_settings.get(
            SETTING_AUTOCOMPILE, settings.get(SETTING_AUTOCOMPILE, True)
        ),
        'base_dir': project_settings.get(
            SETTING_LESSBASEDIR, settings.get(SETTING_LESSBASEDIR)
        ),
        'ignore_underscored': project_settings.get(
            SETTING_IGNOREPREFIXEDFILES,
            settings.get(SETTING_IGNOREPREFIXEDFILES, False)
        ),
        'lessc_command': project_settings.get(
            SETTING_LESSCCOMMAND,
            settings.get(SETTING_LESSCCOMMAND)
        ),
        'main_file': project_settings.get(
            SETTING_MAINFILE,
            settings.get(SETTING_MAINFILE, False)
        ),
        'media_group': project_settings.get(
            SETTING_MEDIA_GROUP,
            settings.get(SETTING_MEDIA_GROUP, True)
        ),
        'minimised': project_settings.get(
            SETTING_MINIFY,
            settings.get(SETTING_MINIFY, True)
        ),
        'min_name': project_settings.get(
            SETTING_MINNAME,
            settings.get(SETTING_MINNAME, True)
        ),
        'output_dir': project_settings.get(
            SETTING_OUTPUTDIR,
            settings.get(SETTING_OUTPUTDIR)
        ),
        'output_file': project_settings.get(
            SETTING_OUTPUTFILE,
            settings.get(SETTING_OUTPUTFILE)
        ),
        'create_css_source_maps': project_settings.get(
            SETTING_CREATECSSSOURCEMAPS,
            settings.get(SETTING_CREATECSSSOURCEMAPS)
        ),
        'autoprefix': project_settings.get(
            SETTING_AUTOPREFIX,
            settings.get(SETTING_AUTOPREFIX)
        ),
        'disable_verbose': project_settings.get(
            SETTING_DISABLEVERBOSE,
            settings.get(SETTING_DISABLEVERBOSE)
        ),
        'silent': project_settings.get(
            SETTING_SILENT,
            settings.get(SETTING_SILENT)
        ),
    }

    # Get the filename and encode accordingly.
    self.file_name = self.view.file_name()
    if sys.version_info < (3, 0, 0):
        if IS_WINDOWS:
            self.file_name = self.file_name.encode(
                sys.getfilesystemencoding()
            )
        else:
            self.file_name = self.file_name.encode("utf_8")

def output_file_name(self, file_name):
    # check if an output file has been specified
    output_file = self.settings.get('output_file', '')
    if output_file:
        return output_file if output_file.endswith('.css') else \
            '{}.css'.format(output_file)
    else:
        # when no output file is specified we take the name of the less
        # file and substitute .less with .css
        if self.settings['min_name']:
            return re.sub('\.less$', '.min.css', file_name)
        else:
            return re.sub('\.less$', '.css', file_name)

def convert_one(self, is_auto_save=False):
    """
    Used in the commands 'LessToCssCommand' and 'AutoLessToCssCommand'
    :param is_auto_save:
    :return:
    """

    # if this method is called on auto save but auto compile is not enabled,
    # stop processing the file
    if is_auto_save and not self.settings['auto_compile']:
        return ''

    # check if files starting with an underscore should be ignored and
    # if the file name starts with an underscore
    if (self.settings['ignore_underscored'] and \
        os.path.basename(self.file_name).startswith('_') and \
        is_auto_save and not self.settings['main_file']):
        print(
            "[less2css] {} ignored, file name starts with an underscore "
            "and ignorePrefixedFiles is True".format(self.file_name)
        )
        return ''

    dirs = self.parseBaseDirs(
        self.settings['base_dir'],
        self.settings['output_dir']
    )

    # if you've set the main_file (relative to current file), only that file
    # gets compiled this allows you to have one file with lots of @imports
    if self.settings['main_file']:
        self.file_name = os.path.join(
            os.path.dirname(self.file_name),
            self.settings['main_file']
        )

    # compile the LESS file
    return self.convertLess2Css(
        self.settings['lessc_command'],
        dirs=dirs,
        less_file=self.file_name,
    )

# for command 'AllLessToCssCommand'
def convertAll(self):
    err_count = 0

    dirs = self.parseBaseDirs(
        self.settings['base_dir'], self.settings['output_dir']
    )

    for r, d, f in os.walk(dirs['less']):
        # loop through all the files in the folder
        for files in f:
          # only process files ending on .less
            if files.endswith(".less"):
                # check if files starting with an underscore should be
                # ignored and if the file name starts with an underscore
                if (self.settings['ignore_underscored'] and \
                            files.startswith('_')):
                    # print a friendly message for the user
                    print(
                        "[less2css] {} ignored, file name starts with an "
                        "underscore "
                        "and ignorePrefixedFiles is True".format(
                            os.path.join(r, files)
                        )
                    )
                else:
                    # add path to file name
                    fn = os.path.join(r, files)
                    # call compiler
                    resp = self.convertLess2Css(
                        self.settings['lessc_command'],
                        dirs,
                        less_file=fn,
                    )
                    # check the result of the compiler,
                    # if it isn't empty an error has occured
                    if resp:
                        # keep count of the number of files that
                        # failed to compile
                        err_count += 1

    # if err_count is more than 0 it means at least 1 error occurred while
    # compiling all LESS files
    if err_count > 0:
        return "There were errors compiling all LESS files"
    return ''

# old function name keep for legacy
convertOne = convert_one

# do convert
def convertLess2Css(self, lessc_command, dirs, less_file=''):
    args = []

    # get the current file & its css variant
    # if no file was specified take the file name if the current view

    if not less_file:
        less_file = self.file_name

    # if the current file name doesn't end on .less, stop processing it
    if not self.view.file_name().endswith(".less"):
        return ''

    css_file_name = self.output_file_name(less_file)

    # Check if the CSS file should be written to the same folder as
    # where the LESS file is
    css_dir = dirs['css']
    if dirs['same_dir']:
        # set the folder for the CSS file to the same
        # folder as the LESS file
        css_dir = os.path.dirname(less_file)
    elif dirs['shadow_folders']:
        print(
            '[less2css] Using shadowed folders: outputting to {}'.format(
                dirs['css']
            )
        )
        replacement = css_file_name.replace(
            dirs['less'],
            ''
        )
        # Strip off the slash this can cause issue within windows file paths
        css_dir = os.path.join(
            dirs['css'],
            os.path.dirname(replacement.strip('/').strip('\\'))
        )
    # get the file name of the CSS file, including the extension
    sub_path = os.path.basename(css_file_name)  # css file name
    # combine the folder for the CSS file with the file name, this
    # will be our target
    css_file_name = os.path.join(css_dir, sub_path)

    # create directories
    # get the name of the folder where we need to save the CSS file
    output_dir = os.path.dirname(css_file_name)
    # if the folder doesn't exist, create it
    if not os.path.isdir(output_dir):
        os.makedirs(output_dir)

    # if no alternate compiler has been specified, pick the default one
    if (type(lessc_command) is not str) or (not lessc_command):
        lessc_command = 'lessc'
    else:
        print('[less2css] Using less compiler :: {}'.format(lessc_command))
    # check if we're compiling with the default compiler
    if lessc_command == "lessc":
        if IS_WINDOWS:
            # change command from lessc to lessc.cmd on Windows,
            # only lessc.cmd works but lessc doesn't
            lessc_command = 'lessc.cmd'
        else:
            # if is not Windows, modify the PATH
            env = os.getenv('PATH')
            env = env + ':/usr/bin:/usr/local/bin:/usr/local/sbin'
            os.environ['PATH'] = env
            # check for the existance of the less compiler,
            # exit if it can't be located
            if subprocess.call(['which', lessc_command]) == 1:
                return sublime.error_message(
                'less2css error: `lessc` is not available'
                )

    # check if the compiler should create a minified CSS file
    if self.settings['media_group']:
        args.append('--group-css-media-queries')
    
    _minifier = None
    if self.settings['minimised'] is True:
        _minifier = '--clean-css'
    elif type(self.settings.get('minimised')) is str:
        _minifier = self.settings.get('minimised', '')

    if _minifier:
        print('[less2css] Using minifier : '+_minifier)
        args.append(_minifier)

    if self.settings['create_css_source_maps']:
        args.append('--source-map')
        print('[less2css] Using css source maps')

    if self.settings['autoprefix']:
        args.append('--autoprefix')
        print('[less2css] add prefixes to {}'.format(css_file_name))

    # you must opt in to disable this option
    if not self.settings['disable_verbose']:
        args.append('--verbose')
        print('[less2css] Using verbose mode')
        
    if self.settings['silent']:
        args.append('--silent')
        print('[less2css] Disabled warnings')
    
    print("[less2css] Converting " + less_file + " to " + css_file_name)

    command = list(
        itertools.chain.from_iterable(
            [[lessc_command], args, [less_file, css_file_name]]
        )
    )

    #run compiler, catch an errors that might come up
    try:
        # not sure if node outputs on stderr or stdout so capture both
        if IS_WINDOWS:
            # this startupinfo structure prevents a console window from popping up on Windows
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            p = subprocess.Popen(
                command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo
            )
        else:    
            p = subprocess.Popen(
                command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )
    except OSError as err:
        # an error has occured, stop processing the file any further
        return sublime.error_message('less2css error: ' + str(err))
    stdout, stderr = p.communicate()

    # create a regex to match all blank lines and control characters
    blank_line_re = re.compile('(^\s+$)|(\033\[[^m]*m)', re.M)

    # take the result text returned from node and convert it to UTF-8
    out = stderr.decode("utf_8")
    # use the regex to replace all blank lines and control characters
    # with an empty string
    out = blank_line_re.sub('', out)

    # if out is empty it means the LESS file was succesfuly compiled to CSS,
    # else we will print the error
    if not out:
        print('[less2css] Convert completed!')
    else:
        print('----[less2cc] Compile Error----')
        print(out)

    return out

def parseBaseDirs(self, base_dir='./', output_dir=''):
    """
     tries to find the project folder and normalize relative paths
     such as /a/b/c/../d to /a/b/d
     the resulting object will have the following members:
       - project_dir (string) = the top level folder of the project which
                                houses the current file
       - less (string)        = the normalised folder specified in the
                                base_dir parameter
       - css (string)         = the normalised folder where the CSS file
                                should be stored
       - sameDir (bool)       = True if the CSS file should be written to
                                the same folder as where the
                                LESS file is located; otherwise False
       - shadowFolders (bool) = True if the CSS files should follow the same
                                folder structure as the LESS files.
    """
    shadow_folders = False
    # make sure we have a base and output dir before we continue.
    # if none were provided we will assign a default
    base_dir = base_dir or './'
    output_dir = output_dir or ''


    # get the folder of the current file
    file_dir = os.path.dirname(self.file_name)

    # current[0] here will be the parent folder, while current[1]
    # is the current folder name
    current = os.path.split(file_dir)

    # if output_dir is set to auto, try to find appropriate destination
    if output_dir == 'auto':
        # parent[1] will be the parent folder name, while parent[0]
        # is the parent's parent path
        parent = os.path.split(current[0])
        # check if the file is located in a folder called less
        if current[1] == 'less':
            # check if the less folder is located in a folder called css
            if parent[1] == 'css':
                # the current folder is less and the parent folder is css,
                # set the css folder as the output dir
                output_dir = current[0]
            elif os.path.isdir(os.path.join(current[0], 'css')):
                # the parent folder of less has a folder named css,
                # make this the output dir
                output_dir = os.path.join(current[0], 'css')
            else:
                # no conditions have been met, compile the file to the same
                # folder as the less file is in
                output_dir = ''
        else:
            # we tried to automate it but failed
            output_dir = ''
    elif output_dir == 'shadow':
        shadow_folders = True
        # replace last occurrence of less with css
        parts = base_dir.rsplit('less', 1)
        output_dir = 'css'.join(parts)

    # find project path
    # you can have multiple folders at the top level in a project but
    # there is no way to get the project folder for the current file.
    # we have to do some work to determine the current project folder
    proj_dir = ''
    window = sublime.active_window()
    # this will get us all the top level folders in the project
    proj_folders = window.folders()
    # loop through all the top level folders in the project
    for folder in proj_folders:
        # we will have found the project folder when it matches
        # with the start of the current file name
        if self.file_name.startswith(folder):
            # keep the current folder as the project folder
            proj_dir = folder
            break

    # normalize less base path
    if not base_dir.startswith('/'):
        base_dir = os.path.normpath(os.path.join(proj_dir, base_dir))

    # if the output_dir is empty or set to ./ it means the CSS file should
    # be placed in the same folder as the LESS file
    if output_dir in ['', './']:
        same_dir = True
    else:
        same_dir = False

    # normalize css output base path
    if not output_dir.startswith('/'):
        output_dir = os.path.normpath(os.path.join(proj_dir, output_dir))

    # return the object with all the information that is needed to be
    # determine where to leave the CSS file when compiling
    return {
        'project': proj_dir,
        'less': base_dir,
        'css': output_dir,
        'same_dir': same_dir,
        'shadow_folders': shadow_folders
    }

`

norm???

mmmDanone avatar Aug 03 '17 21:08 mmmDanone