├── .no-sublime-package ├── messages.json ├── .gitignore ├── messages ├── v0.3.0.txt ├── v0.2.0.txt └── install.txt ├── screenshot.png ├── fix_color_scheme.gif ├── transparency_circle_light.png ├── transparency_circle_mid.png ├── background_fix.xml ├── GutterColor.sublime-commands ├── file.py ├── LICENSE ├── Main.sublime-menu ├── GutterColor.sublime-settings ├── README.md ├── gutter_color.py ├── test.css └── line.py /.no-sublime-package: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "install": "messages/install.txt" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | icons/*.png 2 | icons/ 3 | *.pyc 4 | .cache/ 5 | *.gcfix.tmTheme 6 | -------------------------------------------------------------------------------- /messages/v0.3.0.txt: -------------------------------------------------------------------------------- 1 | This version introduces highlighting of web color names! 2 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggordan/GutterColor/HEAD/screenshot.png -------------------------------------------------------------------------------- /fix_color_scheme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggordan/GutterColor/HEAD/fix_color_scheme.gif -------------------------------------------------------------------------------- /transparency_circle_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggordan/GutterColor/HEAD/transparency_circle_light.png -------------------------------------------------------------------------------- /transparency_circle_mid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggordan/GutterColor/HEAD/transparency_circle_mid.png -------------------------------------------------------------------------------- /background_fix.xml: -------------------------------------------------------------------------------- 1 | 2 | name 3 | GutterColor 4 | scope 5 | gutter_color 6 | settings 7 | 8 | foreground 9 | #ffffff 10 | 11 | 12 | -------------------------------------------------------------------------------- /messages/v0.2.0.txt: -------------------------------------------------------------------------------- 1 | This release fixes light color schemes. This is achieved by copying and then modifying your color scheme. 2 | 3 | To fix your scheme: 4 | 5 | - Open the Command Palette 6 | - Select the `GutterColor: Fix Current Color Scheme` command and hit return 7 | - GutterColor will magically fix your scheme 8 | -------------------------------------------------------------------------------- /messages/install.txt: -------------------------------------------------------------------------------- 1 | Thanks for installing GutterColor! 2 | 3 | Before you get started you'll need ImageMagick installed. You can get this from the website: http://www.imagemagick.org/ 4 | 5 | You need to configure the convert_path before things start working. This can be done through Preferences/Package Settings/GutterColor/Settings - User 6 | -------------------------------------------------------------------------------- /GutterColor.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "GutterColor: Clear Cache", 4 | "command": "gutter_color_clear_cache" 5 | }, 6 | { 7 | "caption": "GutterColor: Fix Current Color Scheme", 8 | "command": "gutter_color_fix_current_scheme" 9 | }, 10 | { 11 | "caption": "Preferences: GutterColor Settings – Default", 12 | "command": "open_file", "args": { 13 | "file": "${packages}/Gutter Color/GutterColor.sublime-settings" 14 | } 15 | }, 16 | { 17 | "caption": "Preferences: GutterColor Settings – User", 18 | "command": "open_file", "args": { 19 | "file": "${packages}/User/GutterColor.sublime-settings" 20 | } 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /file.py: -------------------------------------------------------------------------------- 1 | from sublime import Region 2 | from threading import Thread 3 | from .line import Line 4 | 5 | class File(): 6 | """ A File corresponds to one sublime.View """ 7 | 8 | def __init__(self, view, action = 'initialize'): 9 | self.view = view # sublime.View 10 | self.id = self.view.buffer_id() 11 | self.action = action 12 | self.scan() 13 | 14 | def scan(self): 15 | """Scan the file for colours and add/remove regions appropriately""" 16 | 17 | # Return a list of all the line regions in the current file 18 | if self.action == 'update': 19 | regions = [self.view.line(s) for s in self.view.sel()] 20 | else: 21 | regions = self.view.lines(Region(0, self.view.size())) 22 | 23 | for region in regions: 24 | line = Line(self.view, region, self.id) 25 | if line.has_color(): 26 | line.add_region() 27 | else: 28 | try: 29 | self.view.erase_regions("gutter_color_%s" % region.a) 30 | except: 31 | pass 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Gordan Grasarevic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /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": "GutterColor", 16 | "children": 17 | [ 18 | { 19 | "command": "open_file", 20 | "args": { 21 | "file": "${packages}/Gutter Color/GutterColor.sublime-settings" 22 | }, 23 | "caption": "Settings - Default" 24 | }, 25 | { 26 | "command": "open_file", 27 | "args": { 28 | "file": "${packages}/User/GutterColor.sublime-settings" 29 | }, 30 | "caption": "Settings - User" 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | ] 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /GutterColor.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | 3 | /* 4 | * The location of the ImageMagic convert script. 5 | */ 6 | "convert_path" : "/usr/bin/convert", 7 | 8 | /* 9 | * The syntax for which to run GutterColor. 10 | */ 11 | "supported_syntax": ["css", "scss", "sass", "less", "stylus", "css3", "xml"], 12 | 13 | /* 14 | * Whether to render images with an alpha channel. 15 | * Setting false renders all colors as opaque, true composites 16 | * colors onto a gray checkered background, and "light" is 17 | * the same as true, but with a brighter BG texture 18 | */ 19 | "use_transparency": true, 20 | 21 | /* 22 | * Whether Gutter Color should automatically create a new version of the active 23 | * color scheme that doesn't darken gutter icons. (common on light backgrounds) 24 | * This should be a boolean or an array of strings. 25 | * 26 | * Valid strings are "syntax-specific"/"syntax", "global"/"user", and settings 27 | * file names (e.g. "Packages/SomePlugin/SomePlugin.sublime-settings"). 28 | * "fix_color_schemes": true is eqivalent to ["global", "syntax-specific"] 29 | */ 30 | "fix_color_schemes": false, 31 | 32 | /* 33 | * Whether Gutter Color should search for and render colors for color names, 34 | * e.g. for use with CSS3 color names. 35 | */ 36 | "textual_colors": true, 37 | 38 | /* 39 | * Additional user-defined color matches. 40 | */ 41 | /* 42 | "custom_colors": [ 43 | { 44 | // The rule to match 45 | "regex": "(['\"])((?:[0-9a-fA-F]{3}){1,2}(?![0-9a-fA-F]+))\\1", 46 | // The capture group to use; defaults to 0 (the whole expression) 47 | "group_id": 2, 48 | // Strings to be concatenated before sending to ImageMagic 49 | "output_prefix": "#", 50 | "output_suffix": "" 51 | },{ 52 | "regex": "color=([0-9a-fA-F]{6})", 53 | "group_id": 1, 54 | "output_prefix": "#" 55 | }] 56 | */ 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gutter Color 2 | 3 | Gutter Color is a cross-platform Sublime Text plugin which displays a colored icon for all lines which contain a color. 4 | 5 | ![GutterColor](screenshot.png) 6 | 7 | ## Requirements 8 | 9 | * [ImageMagick](http://www.imagemagick.org/) 10 | 11 | ## Configure 12 | 13 | * Install ImageMagick 14 | * Set the `convert_path` in `Preferences: Package Settings > GutterColor > Settings – User` to the location of the ImageMagick `convert` script: 15 | 16 | ```json 17 | { 18 | "convert_path" : "/usr/local/bin/convert" 19 | } 20 | ``` 21 | * For help with either, view [this blog post by Wes Bos](http://wesbos.com/css-gutter-color-sublime-text/) 22 | * *Additional user settings:* 23 | * `supported_syntax` sets the filetypes the script runs in. 24 | * `use_transparency` determines whether to draw RGBA, HSLA, and custom formats with alpha channels enabled, compositing the fill color over a checkered gray background. Setting this to `"light"` is the same as `true`, but uses a lighter checkerboard. 25 | * `custom_colors` adds additional regexes for custom color string formats, along with associated string matching and affixing rules. (See default settings file.) 26 | * `fix_color_schemes` enables automatic editing of the current tmTheme to correct ST's icon tinting. (See **Icon Tinting and Light Color Schemes** below.) 27 | 28 | ## Troubleshooting 29 | 30 | #### Fixing ImageMagick on OSX 31 | If you're experiencing issues with ImageMagick (installed via brew) when using GutterColor on OSX, follow these instructions to fix it. 32 | 33 | 1. Uninstall IM with `brew uninstall imagemagick` 34 | 2. Install again with a couple of options `brew install imagemagick --with-xz --with-font-config --with-little-cms --with-little-cms2` 35 | 3. Check the location of `convert` with `type convert` 36 | 4. Copy the location and edit the GutterColor/Settings - User `convert_path` to be the value of step 3. 37 | 38 | #### Icon Tinting and Light Color Schemes 39 | Sublime Text automatically tints gutter icons to match your background color settings. Unfortunately this can cause Gutter Color's samples to vary from slightly inaccurate to completely black. To counteract this effect, Gutter Color can generate a new copy of your current color scheme that sets the background to black *only for the scope of the plugin*. You will not see any visual changes other than the icons. 40 | 41 | ![Animation of a user running the "Fix Current Color Scheme" command.](fix_color_scheme.gif) 42 | 43 | * To manually update a color scheme, open the Command Palette and select `GutterColor: Fix Current Color Scheme`. 44 | * Alternatively, the plugin can run this process automatically via the setting `fix_color_schemes`. 45 | 46 | #### Other Display Issues 47 | 48 | Sublime's placeholder image is a red-and-yellow-striped box. If you are seeing these, it is most likely that Gutter Color cannot find ImageMagick to generate the samples. Double-check that you have your `convert_path` setting and/or your `PATH` environment variable set correctly, and restart Sublime Text. If the problem persists, feel free to [contact us](https://github.com/ggordan/GutterColor/issues). 49 | 50 | ## TODO 51 | 52 | * Add support for SASS/LESS variables 53 | * Backport to ST2 54 | * Handle conflicts with GitGutter/VCS Gutter 55 | * Support for CSS gradients 56 | * Better cleanup processes 57 | 58 | ## Thanks 59 | Thanks to all of the [contributors](https://github.com/ggordan/GutterColor/graphs/contributors) who continue to improve GutterColor! 60 | 61 | # License 62 | 63 | [MIT](/LICENSE). 64 | -------------------------------------------------------------------------------- /gutter_color.py: -------------------------------------------------------------------------------- 1 | from .file import File 2 | from sublime_plugin import EventListener, WindowCommand, TextCommand 3 | from sublime import load_settings, save_settings, load_resource, packages_path 4 | 5 | def clear_cache(force = False): 6 | """ 7 | If the folder exists, and has more than 5MB of icons in the cache, delete 8 | it to clear all the icons then recreate it. 9 | """ 10 | from os.path import getsize, join, isfile, exists 11 | from os import makedirs, listdir 12 | from sublime import cache_path 13 | from shutil import rmtree 14 | 15 | # The icon cache path 16 | icon_path = join(cache_path(), "GutterColor") 17 | 18 | # The maximum amount of space to take up 19 | limit = 5242880 # 5 MB 20 | 21 | if exists(icon_path): 22 | size = sum(getsize(join(icon_path, f)) for f in listdir(icon_path) if isfile(join(icon_path, f))) 23 | if force or (size > limit): rmtree(icon_path) 24 | 25 | if not exists(icon_path): makedirs(icon_path) 26 | 27 | def plugin_loaded(): 28 | clear_cache() 29 | fix_schemes_in_windows() 30 | 31 | class GutterColorClearCacheCommand(WindowCommand): 32 | def run(self): 33 | clear_cache(True) 34 | 35 | class GutterColorEventListener(EventListener): 36 | """Scan the view when it gains focus, and when it is saved.""" 37 | 38 | def on_activated_async(self, view): 39 | """Scan file when it gets focus""" 40 | if syntax(view) in settings().get('supported_syntax'): 41 | fix_scheme_in_view(view) 42 | File(view) 43 | 44 | def on_modified(self, view): 45 | """Scan file when it is modified""" 46 | if syntax(view) in settings().get('supported_syntax'): 47 | File(view, 'update') 48 | 49 | def on_pre_save_async(self, view): 50 | """Scan file before it is saved""" 51 | if syntax(view) in settings().get('supported_syntax'): 52 | File(view, 'update') 53 | 54 | def settings(): 55 | """Shortcut to the settings""" 56 | return load_settings("GutterColor.sublime-settings") 57 | 58 | def syntax(view): 59 | """Return the view syntax""" 60 | syntax = view.settings().get('syntax') 61 | return syntax.split('/')[-1].split('.')[0].lower() if syntax is not None else "plain text" 62 | 63 | def current_directory(full=False): 64 | """Return the name of the directory containing this plugin""" 65 | from os.path import dirname, realpath, split 66 | if full: 67 | return dirname(realpath(__file__)) 68 | else: 69 | return split(dirname(realpath(__file__)))[1] 70 | 71 | def fix_schemes_in_windows(): 72 | """Change color schemes for all current views in the supported syntax list""" 73 | from sublime import windows 74 | for window in windows(): 75 | for view in window.views(): 76 | if syntax(view) in settings().get('supported_syntax'): 77 | fix_scheme_in_view(view) 78 | 79 | def fix_scheme_in_view(view, regenerate=False, ignore_flags=False): 80 | """Change color scheme in settings relevant to current view""" 81 | fix_flag = settings().get("fix_color_schemes", False) 82 | (fix_syntax, fix_global, fix_custom) = (False, False, False) 83 | custom_files = [] 84 | if fix_flag == True: 85 | (fix_syntax, fix_global) = (True, True) 86 | elif isinstance(fix_flag, list): 87 | for label in fix_flag: 88 | if label in ("syntax", "syntax-specific"): 89 | fix_syntax = True 90 | if label in ("user", "global", "preferences"): 91 | fix_global = True 92 | if ".sublime-settings" in label: 93 | fix_custom = True 94 | custom_files.append(label) 95 | elif ignore_flags: 96 | pass # otherwise we might quit when we want to force a check contrary to user prefs 97 | else: 98 | return # setting is false, nonexistant, or malformed, so exit 99 | 100 | current_scheme = view.settings().get("color_scheme") 101 | modified_marker = ".gcfix." 102 | if modified_marker in current_scheme: 103 | if regenerate: 104 | new_scheme = current_scheme 105 | else: 106 | return # this view already has a fixed scheme and we aren't regenerating, so exit 107 | else: 108 | new_scheme = "Packages/"+current_directory()+"/"+current_scheme.split("/")[-1].split(".")[0]+\ 109 | modified_marker + current_scheme.split(".")[-1] 110 | 111 | if fix_custom: 112 | for custom_filename in custom_files: 113 | if fix_scheme_in_settings(custom_filename, current_scheme, new_scheme): 114 | return 115 | if fix_syntax or ignore_flags: 116 | syntax_filename = view.settings().get('syntax').split('/')[-1].split('.')[0] + ".sublime-settings" 117 | if fix_scheme_in_settings(syntax_filename, current_scheme, new_scheme): 118 | return 119 | if fix_global or ignore_flags: 120 | if fix_scheme_in_settings("Preferences.sublime-settings", current_scheme, new_scheme): 121 | return 122 | print("Could not find or access the settings file where current color_scheme ("+current_scheme+") is set.") 123 | 124 | def fix_scheme_in_settings(settings_file,current_scheme, new_scheme, regenerate=False): 125 | """Change the color scheme in the given Settings to a background-corrected one""" 126 | from os.path import join, normpath, isfile 127 | 128 | settings = load_settings(settings_file) 129 | settings_scheme = settings.get("color_scheme") 130 | if current_scheme == settings_scheme: 131 | new_scheme_path = join(packages_path(), normpath(new_scheme[len("Packages/"):])) 132 | if isfile(new_scheme_path) and not regenerate: 133 | settings.set("color_scheme", new_scheme) 134 | else: 135 | generate_scheme_fix(current_scheme, new_scheme_path) 136 | settings.set("color_scheme", new_scheme) 137 | save_settings(settings_file) 138 | return True 139 | return False 140 | 141 | def generate_scheme_fix(old_scheme, new_scheme_path): 142 | """Appends background-correction XML to a color scheme file""" 143 | from os.path import join 144 | from re import sub 145 | UUID_REGEX = '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}' 146 | 147 | with open(join(packages_path(),current_directory(),'background_fix.xml')) as f: 148 | xml = f.read() 149 | scheme_data = load_resource(old_scheme) # only valid for ST3 API! 150 | 151 | insertion_point = scheme_data.rfind("") 152 | new_scheme_data = scheme_data[:insertion_point] + xml + scheme_data[insertion_point:] 153 | 154 | def uuid_gen(args): 155 | from uuid import uuid4 156 | return str(uuid4()) 157 | new_scheme_data = sub(UUID_REGEX, uuid_gen, new_scheme_data) 158 | 159 | with open(new_scheme_path, "wb") as f: 160 | f.write(new_scheme_data.encode("utf-8")) 161 | 162 | class GutterColorFixCurrentScheme(TextCommand): 163 | def run(self, args): 164 | fix_scheme_in_view(self.view, True, True) 165 | -------------------------------------------------------------------------------- /test.css: -------------------------------------------------------------------------------- 1 | /* From https://developer.mozilla.org/en-US/docs/Web/CSS/color_value */ 2 | 3 | .COLORS { 4 | background-color: black; 5 | background-color: gray; 6 | background-color: white; 7 | background-color: maroon; 8 | background-color: red; 9 | background-color: purple; 10 | background-color: fuchsia; 11 | background-color: green; 12 | background-color: lime; 13 | background-color: olive; 14 | background-color: yellow; 15 | background-color: navy; 16 | background-color: blue; 17 | background-color: teal; 18 | background-color: aqua; 19 | } 20 | 21 | .HEX3 { 22 | background-color: #000; /* black */ 23 | background-color: #888; /* gray */ 24 | background-color: #FFF; /* white */ 25 | background-color: #800; /* maroon */ 26 | background-color: #F00; /* red */ 27 | background-color: #808; /* purple */ 28 | background-color: #F0F; /* fuchsia */ 29 | background-color: #080; /* green */ 30 | background-color: #0F0; /* lime */ 31 | background-color: #880; /* olive */ 32 | background-color: #FF0; /* yellow */ 33 | background-color: #008; /* navy */ 34 | background-color: #00F; /* blue */ 35 | background-color: #088; /* teal */ 36 | background-color: #0FF; /* aqua */ 37 | } 38 | 39 | .HEX3LOWER { 40 | background-color: #000; /* black */ 41 | background-color: #888; /* gray */ 42 | background-color: #fff; /* white */ 43 | background-color: #800; /* maroon */ 44 | background-color: #f00; /* red */ 45 | background-color: #808; /* purple */ 46 | background-color: #f0f; /* fuchsia */ 47 | background-color: #080; /* green */ 48 | background-color: #0f0; /* lime */ 49 | background-color: #880; /* olive */ 50 | background-color: #ff0; /* yellow */ 51 | background-color: #008; /* navy */ 52 | background-color: #00f; /* blue */ 53 | background-color: #088; /* teal */ 54 | background-color: #0ff; /* aqua */ 55 | } 56 | 57 | .HEX6 { 58 | background-color: #000000; /* black */ 59 | background-color: #888888; /* gray */ 60 | background-color: #FFFFFF; /* white */ 61 | background-color: #880000; /* maroon */ 62 | background-color: #FF0000; /* red */ 63 | background-color: #880088; /* purple */ 64 | background-color: #FF00FF; /* fuchsia */ 65 | background-color: #008800; /* green */ 66 | background-color: #00FF00; /* lime */ 67 | background-color: #888800; /* olive */ 68 | background-color: #FFFF00; /* yellow */ 69 | background-color: #000088; /* navy */ 70 | background-color: #0000FF; /* blue */ 71 | background-color: #008888; /* teal */ 72 | background-color: #00FFFF; /* aqua */ 73 | } 74 | 75 | .HEX6LOWER { 76 | background-color: #000000; /* black */ 77 | background-color: #888888; /* gray */ 78 | background-color: #ffffff; /* white */ 79 | background-color: #880000; /* maroon */ 80 | background-color: #ff0000; /* red */ 81 | background-color: #880088; /* purple */ 82 | background-color: #ff00ff; /* fuchsia */ 83 | background-color: #008800; /* green */ 84 | background-color: #00ff00; /* lime */ 85 | background-color: #888800; /* olive */ 86 | background-color: #ffff00; /* yellow */ 87 | background-color: #000088; /* navy */ 88 | background-color: #0000ff; /* blue */ 89 | background-color: #008888; /* teal */ 90 | background-color: #00ffff; /* aqua */ 91 | } 92 | 93 | .RGB { 94 | background-color: rgb( 0, 0, 0); /* black */ 95 | background-color: rgb(128, 128, 128); /* gray */ 96 | background-color: rgb(255, 255, 255); /* white */ 97 | background-color: rgb(128, 0, 0); /* maroon */ 98 | background-color: rgb(255, 0, 0); /* red */ 99 | background-color: rgb(128, 0, 128); /* purple */ 100 | background-color: rgb(255, 0, 255); /* fuchsia */ 101 | background-color: rgb( 0, 128, 0); /* green */ 102 | background-color: rgb( 0, 255, 0); /* lime */ 103 | background-color: rgb(128, 128, 0); /* olive */ 104 | background-color: rgb(255, 255, 0); /* yellow */ 105 | background-color: rgb( 0, 0, 128); /* navy */ 106 | background-color: rgb( 0, 0, 255); /* blue */ 107 | background-color: rgb( 0, 128, 128); /* teal */ 108 | background-color: rgb( 0, 255, 255); /* aqua */ 109 | } 110 | 111 | .RGBA { 112 | background-color: rgba( 0, 0, 0, 0.3); /* black */ 113 | background-color: rgba(128, 128, 128, 0.3); /* gray */ 114 | background-color: rgba(255, 255, 255, 0.3); /* white */ 115 | background-color: rgba(128, 0, 0, 0.3); /* maroon */ 116 | background-color: rgba(255, 0, 0, 0.3); /* red */ 117 | background-color: rgba(128, 0, 128, 0.3); /* purple */ 118 | background-color: rgba(255, 0, 255, 0.3); /* fuchsia */ 119 | background-color: rgba( 0, 128, 0, 0.3); /* green */ 120 | background-color: rgba( 0, 255, 0, 0.3); /* lime */ 121 | background-color: rgba(128, 128, 0, 0.3); /* olive */ 122 | background-color: rgba(255, 255, 0, 0.3); /* yellow */ 123 | background-color: rgba( 0, 0, 128, 0.3); /* navy */ 124 | background-color: rgba( 0, 0, 255, 0.3); /* blue */ 125 | background-color: rgba( 0, 128, 128, 0.3); /* teal */ 126 | background-color: rgba( 0, 255, 255, 0.3); /* aqua */ 127 | } 128 | 129 | 130 | .HSL { 131 | background-color: hsl( 0, 100%, 50%); /* red */ 132 | background-color: hsl( 30, 100%, 50%); /* orange */ 133 | background-color: hsl( 60, 100%, 50%); /* yellow */ 134 | background-color: hsl( 90, 100%, 50%); /* green */ 135 | background-color: hsl(120, 100%, 50%); /* green */ 136 | background-color: hsl(150, 100%, 50%); /* green */ 137 | background-color: hsl(180, 100%, 50%); /* blue */ 138 | background-color: hsl(210, 100%, 50%); /* blue */ 139 | background-color: hsl(240, 100%, 50%); /* blue */ 140 | background-color: hsl(270, 100%, 50%); /* purple */ 141 | background-color: hsl(300, 100%, 50%); /* pink */ 142 | background-color: hsl(330, 100%, 50%); /* pinkish */ 143 | background-color: hsl(360, 100%, 50%); /* red */ 144 | background-color: hsl(120, 100%, 25%); /* dark green */ 145 | background-color: hsl(120, 100%, 50%); /* green */ 146 | background-color: hsl(120, 100%, 75%); /* light green */ 147 | background-color: hsl(120, 100%, 50%); /* green */ 148 | background-color: hsl(120, 67%, 50%); /* green */ 149 | background-color: hsl(120, 33%, 50%); /* green */ 150 | background-color: hsl(120, 0%, 50%); /* gray */ 151 | background-color: hsl(120, 60%, 70%); /* pastel green */ 152 | } 153 | 154 | .HSLA { 155 | background-color: hsla( 0, 100%, 50%, 0.3); /* red */ 156 | background-color: hsla( 30, 100%, 50%, 0.3); /* orange */ 157 | background-color: hsla( 60, 100%, 50%, 0.3); /* yellow */ 158 | background-color: hsla( 90, 100%, 50%, 0.3); /* green */ 159 | background-color: hsla(120, 100%, 50%, 0.3); /* green */ 160 | background-color: hsla(150, 100%, 50%, 0.3); /* green */ 161 | background-color: hsla(180, 100%, 50%, 0.3); /* blue */ 162 | background-color: hsla(210, 100%, 50%, 0.3); /* blue */ 163 | background-color: hsla(240, 100%, 50%, 0.3); /* blue */ 164 | background-color: hsla(270, 100%, 50%, 0.3); /* purple */ 165 | background-color: hsla(300, 100%, 50%, 0.3); /* pink */ 166 | background-color: hsla(330, 100%, 50%, 0.3); /* pinkish */ 167 | background-color: hsla(360, 100%, 50%, 0.3); /* red */ 168 | background-color: hsla(120, 100%, 25%, 0.3); /* dark green */ 169 | background-color: hsla(120, 100%, 50%, 0.3); /* green */ 170 | background-color: hsla(120, 100%, 75%, 0.3); /* light green */ 171 | background-color: hsla(120, 100%, 50%, 0.3); /* green */ 172 | background-color: hsla(120, 67%, 50%, 0.3); /* green */ 173 | background-color: hsla(120, 33%, 50%, 0.3); /* green */ 174 | background-color: hsla(120, 0%, 50%, 0.3); /* gray */ 175 | background-color: hsla(120, 60%, 70%, 0.3); /* pastel green */ 176 | } 177 | -------------------------------------------------------------------------------- /line.py: -------------------------------------------------------------------------------- 1 | from os.path import join, dirname, realpath, isfile 2 | from sublime import HIDDEN, PERSISTENT, load_settings, cache_path 3 | import subprocess, os, glob, re, platform 4 | 5 | css3_names_to_hex = {'aliceblue': '#f0f8ff', 6 | 'antiquewhite': '#faebd7', 7 | 'aqua': '#00ffff', 8 | 'aquamarine': '#7fffd4', 9 | 'azure': '#f0ffff', 10 | 'beige': '#f5f5dc', 11 | 'bisque': '#ffe4c4', 12 | 'black': '#000000', 13 | 'blanchedalmond': '#ffebcd', 14 | 'blue': '#0000ff', 15 | 'blueviolet': '#8a2be2', 16 | 'brown': '#a52a2a', 17 | 'burlywood': '#deb887', 18 | 'cadetblue': '#5f9ea0', 19 | 'chartreuse': '#7fff00', 20 | 'chocolate': '#d2691e', 21 | 'coral': '#ff7f50', 22 | 'cornflowerblue': '#6495ed', 23 | 'cornsilk': '#fff8dc', 24 | 'crimson': '#dc143c', 25 | 'cyan': '#00ffff', 26 | 'darkblue': '#00008b', 27 | 'darkcyan': '#008b8b', 28 | 'darkgoldenrod': '#b8860b', 29 | 'darkgray': '#a9a9a9', 30 | 'darkgrey': '#a9a9a9', 31 | 'darkgreen': '#006400', 32 | 'darkkhaki': '#bdb76b', 33 | 'darkmagenta': '#8b008b', 34 | 'darkolivegreen': '#556b2f', 35 | 'darkorange': '#ff8c00', 36 | 'darkorchid': '#9932cc', 37 | 'darkred': '#8b0000', 38 | 'darksalmon': '#e9967a', 39 | 'darkseagreen': '#8fbc8f', 40 | 'darkslateblue': '#483d8b', 41 | 'darkslategray': '#2f4f4f', 42 | 'darkslategrey': '#2f4f4f', 43 | 'darkturquoise': '#00ced1', 44 | 'darkviolet': '#9400d3', 45 | 'deeppink': '#ff1493', 46 | 'deepskyblue': '#00bfff', 47 | 'dimgray': '#696969', 48 | 'dimgrey': '#696969', 49 | 'dodgerblue': '#1e90ff', 50 | 'firebrick': '#b22222', 51 | 'floralwhite': '#fffaf0', 52 | 'forestgreen': '#228b22', 53 | 'fuchsia': '#ff00ff', 54 | 'gainsboro': '#dcdcdc', 55 | 'ghostwhite': '#f8f8ff', 56 | 'gold': '#ffd700', 57 | 'goldenrod': '#daa520', 58 | 'gray': '#808080', 59 | 'grey': '#808080', 60 | 'green': '#008000', 61 | 'greenyellow': '#adff2f', 62 | 'honeydew': '#f0fff0', 63 | 'hotpink': '#ff69b4', 64 | 'indianred': '#cd5c5c', 65 | 'indigo': '#4b0082', 66 | 'ivory': '#fffff0', 67 | 'khaki': '#f0e68c', 68 | 'lavender': '#e6e6fa', 69 | 'lavenderblush': '#fff0f5', 70 | 'lawngreen': '#7cfc00', 71 | 'lemonchiffon': '#fffacd', 72 | 'lightblue': '#add8e6', 73 | 'lightcoral': '#f08080', 74 | 'lightcyan': '#e0ffff', 75 | 'lightgoldenrodyellow': '#fafad2', 76 | 'lightgray': '#d3d3d3', 77 | 'lightgrey': '#d3d3d3', 78 | 'lightgreen': '#90ee90', 79 | 'lightpink': '#ffb6c1', 80 | 'lightsalmon': '#ffa07a', 81 | 'lightseagreen': '#20b2aa', 82 | 'lightskyblue': '#87cefa', 83 | 'lightslategray': '#778899', 84 | 'lightslategrey': '#778899', 85 | 'lightsteelblue': '#b0c4de', 86 | 'lightyellow': '#ffffe0', 87 | 'lime': '#00ff00', 88 | 'limegreen': '#32cd32', 89 | 'linen': '#faf0e6', 90 | 'magenta': '#ff00ff', 91 | 'maroon': '#800000', 92 | 'mediumaquamarine': '#66cdaa', 93 | 'mediumblue': '#0000cd', 94 | 'mediumorchid': '#ba55d3', 95 | 'mediumpurple': '#9370d8', 96 | 'mediumseagreen': '#3cb371', 97 | 'mediumslateblue': '#7b68ee', 98 | 'mediumspringgreen': '#00fa9a', 99 | 'mediumturquoise': '#48d1cc', 100 | 'mediumvioletred': '#c71585', 101 | 'midnightblue': '#191970', 102 | 'mintcream': '#f5fffa', 103 | 'mistyrose': '#ffe4e1', 104 | 'moccasin': '#ffe4b5', 105 | 'navajowhite': '#ffdead', 106 | 'navy': '#000080', 107 | 'oldlace': '#fdf5e6', 108 | 'olive': '#808000', 109 | 'olivedrab': '#6b8e23', 110 | 'orange': '#ffa500', 111 | 'orangered': '#ff4500', 112 | 'orchid': '#da70d6', 113 | 'palegoldenrod': '#eee8aa', 114 | 'palegreen': '#98fb98', 115 | 'paleturquoise': '#afeeee', 116 | 'palevioletred': '#d87093', 117 | 'papayawhip': '#ffefd5', 118 | 'peachpuff': '#ffdab9', 119 | 'peru': '#cd853f', 120 | 'pink': '#ffc0cb', 121 | 'plum': '#dda0dd', 122 | 'powderblue': '#b0e0e6', 123 | 'purple': '#800080', 124 | 'red': '#ff0000', 125 | 'rosybrown': '#bc8f8f', 126 | 'royalblue': '#4169e1', 127 | 'saddlebrown': '#8b4513', 128 | 'salmon': '#fa8072', 129 | 'sandybrown': '#f4a460', 130 | 'seagreen': '#2e8b57', 131 | 'seashell': '#fff5ee', 132 | 'sienna': '#a0522d', 133 | 'silver': '#c0c0c0', 134 | 'skyblue': '#87ceeb', 135 | 'slateblue': '#6a5acd', 136 | 'slategray': '#708090', 137 | 'slategrey': '#708090', 138 | 'snow': '#fffafa', 139 | 'springgreen': '#00ff7f', 140 | 'steelblue': '#4682b4', 141 | 'tan': '#d2b48c', 142 | 'teal': '#008080', 143 | 'thistle': '#d8bfd8', 144 | 'tomato': '#ff6347', 145 | 'turquoise': '#40e0d0', 146 | 'violet': '#ee82ee', 147 | 'wheat': '#f5deb3', 148 | 'white': '#ffffff', 149 | 'whitesmoke': '#f5f5f5', 150 | 'yellow': '#ffff00', 151 | 'yellowgreen': '#9acd32'} 152 | 153 | class Line: 154 | 155 | # A digit is one-three numbers, with an optional floating point part, 156 | # and maybe a % sign, and maybe surrounded by whitespace. 157 | DIGIT = '\s*\d{1,3}(\.\d*)?%?\s*' 158 | 159 | # Three digits is three digits, with commas between. 160 | THREE_DIGITS = DIGIT + ',' + DIGIT + ',' + DIGIT 161 | 162 | # Four digits is three digits (which we save for later), 163 | # and then a comma and then the fourth digit. 164 | FOUR_DIGITS = '(' + THREE_DIGITS + '),' + DIGIT 165 | 166 | HEX_REGEXP = '#((?:[0-9a-fA-F]{3}){1,2}(?![0-9a-fA-F]+))' 167 | RGB_REGEXP = 'rgb\(' + THREE_DIGITS + '\)' 168 | RGBA_REGEXP = 'rgba\(' + FOUR_DIGITS + '\)' 169 | HSL_REGEXP = 'hsl\(' + THREE_DIGITS + '\)' 170 | HSLA_REGEXP = 'hsla\(' + FOUR_DIGITS + '\)' 171 | WEB_COLORS_REGEX = '' 172 | WEB_COLORS = [] 173 | 174 | def __init__(self, view, region, file_id): 175 | self.generate_webcolors() 176 | 177 | self.view = view 178 | self.region = region 179 | self.file_id = file_id 180 | self.settings = load_settings("GutterColor.sublime-settings") 181 | self.text = self.view.substr(self.region) 182 | 183 | def has_color(self): 184 | """Returns True/False depending on whether the line has a color in it""" 185 | return True if self.color() else False 186 | 187 | def color(self): 188 | """Returns the color in the line, if any.""" 189 | textual_colors = self.settings.get("textual_colors") 190 | if self.hex_color(): 191 | return self.hex_color() 192 | if self.rgb_color(): 193 | return self.rgb_color() 194 | if self.rgba_color(): 195 | return self.rgba_color() 196 | if self.hsl_color(): 197 | return self.hsl_color() 198 | if self.hsla_color(): 199 | return self.hsla_color() 200 | if textual_colors and self.web_color(): 201 | return self.web_color() 202 | if not self.settings.get("custom_colors") == None: 203 | return self.custom_color() 204 | 205 | def generate_webcolors(self): 206 | """Generates a list of web color names.""" 207 | self.WEB_COLORS = dict((name, color) for (name, color) in css3_names_to_hex.items()) 208 | self.WEB_COLORS_REGEX = '[\\s:](' + '|'.join(self.WEB_COLORS.keys()) + ')[\\s;]' 209 | 210 | def web_color(self): 211 | """Returns the color in the line, if any CSS color name is found.""" 212 | matches = re.search(self.WEB_COLORS_REGEX, self.text) 213 | if matches: 214 | return matches.group(1) 215 | 216 | def hex_color(self): 217 | """Returns the color in the line, if any hex is found.""" 218 | matches = re.search(Line.HEX_REGEXP, self.text) 219 | if matches: 220 | return matches.group(0) 221 | 222 | def rgb_color(self): 223 | """Returns the color in the line, if any rgb is found.""" 224 | matches = re.search(Line.RGB_REGEXP, self.text) 225 | if matches: 226 | return matches.group(0) 227 | 228 | def rgba_color(self): 229 | """Returns the color in the line, if any rgba is found.""" 230 | matches = re.search(Line.RGBA_REGEXP, self.text) 231 | if matches: 232 | if self.transparency_settings()[0]: 233 | return matches.group(0) 234 | else: 235 | return 'rgb(' + matches.group(1) + ')' 236 | 237 | def hsl_color(self): 238 | """Returns the color in the line, if any hsl is found.""" 239 | matches = re.search(Line.HSL_REGEXP, self.text) 240 | if matches: 241 | return matches.group(0) 242 | 243 | def hsla_color(self): 244 | """Returns the color in the line, if any hsla is found.""" 245 | matches = re.search(Line.HSLA_REGEXP, self.text) 246 | if matches: 247 | if self.transparency_settings()[0]: 248 | return matches.group(0) 249 | else: 250 | return 'hsl(' + matches.group(1) + ')' 251 | 252 | def custom_color(self): 253 | """Returns the color in the line, if any user-defined is found.""" 254 | user_colors = self.settings.get("custom_colors") 255 | for user_color in user_colors: 256 | matches = re.search(user_color["regex"], self.text) 257 | group_id = user_color["group_id"] if "group_id" in user_color else 0 258 | prefix = user_color["output_prefix"] if "output_prefix" in user_color else "" 259 | suffix = user_color["output_suffix"] if "output_suffix" in user_color else "" 260 | if matches: 261 | return prefix+matches.group(group_id)+suffix 262 | 263 | def icon_path(self): 264 | """Returns the absolute path to the icons""" 265 | return join(cache_path(), 'GutterColor', '%s.png' % self.color()) 266 | 267 | def relative_icon_path(self): 268 | """The relative location of the color icon""" 269 | return "Cache/GutterColor/%s.png" % (self.color()) 270 | 271 | def add_region(self): 272 | """Add the icon to the gutter""" 273 | self.create_icon() 274 | self.view.add_regions( 275 | "gutter_color_%s" % self.region.a, 276 | [self.region], 277 | "gutter_color", 278 | self.relative_icon_path(), 279 | HIDDEN | PERSISTENT 280 | ) 281 | 282 | def erase_region(self): 283 | """Remove icon from the gutter""" 284 | self.view.erase_regions("gutter_color_%s" % self.region.a) 285 | 286 | def transparency_settings(self): 287 | from .gutter_color import current_directory 288 | # transparency settings 289 | use_transparency = self.settings.get("use_transparency") 290 | if use_transparency == True: 291 | background_path = os.path.join(current_directory(True),"transparency_circle_mid.png") 292 | elif use_transparency in ("light", "mid"): 293 | background_path = os.path.join(current_directory(True),str("transparency_circle_"+use_transparency+".png")) 294 | print(background_path) 295 | use_transparency = True 296 | else: 297 | use_transparency = False 298 | return (use_transparency, background_path) 299 | 300 | def create_icon(self): 301 | paths = [ 302 | self.settings.get("convert_path"), 303 | "/usr/bin/convert", 304 | "/usr/local/bin", 305 | "/usr/bin" 306 | ] 307 | 308 | if ( platform.system()=="Windows"): 309 | delimiter = ";" 310 | convert_name = "convert.exe" 311 | else: 312 | delimiter = ":" 313 | convert_name = "convert" 314 | 315 | paths.extend(glob.glob('/usr/local/Cellar/imagemagick/*/bin')) 316 | paths.extend(os.environ['PATH'].split(delimiter)) 317 | 318 | convert_path = None 319 | WINDOWS_PATH = os.path.join("C:\\Windows\\System32\\convert.exe") 320 | for path in paths: 321 | if not path.endswith(convert_name): 322 | path = os.path.join(path,convert_name) 323 | if os.path.isfile(path) and os.access(path, os.X_OK) \ 324 | and not os.path.samefile(path, WINDOWS_PATH): 325 | convert_path = path 326 | break 327 | 328 | (use_transparency, background_path) = self.transparency_settings() 329 | 330 | """Create the color icon using ImageMagick convert""" 331 | if not use_transparency: 332 | script = "\"%s\" -units PixelsPerCentimeter -type TrueColorMatte -channel RGBA " \ 333 | "-size 32x32 -alpha transparent xc:none " \ 334 | "-fill \"%s\" -draw \"circle 15,16 8,10\" png32:\"%s\"" % \ 335 | (convert_path, self.color(), self.icon_path()) 336 | else: 337 | script = "\"%s\" \"%s\" -type TrueColorMatte -channel RGBA " \ 338 | "-fill \"%s\" -draw \"circle 15,16 8,10\" png32:\"%s\"" % \ 339 | (convert_path, background_path, self.color(), self.icon_path()) 340 | if not isfile(self.icon_path()): 341 | pr = subprocess.Popen(script, 342 | shell = True, 343 | stdout = subprocess.PIPE, 344 | stderr = subprocess.PIPE, 345 | stdin = subprocess.PIPE) 346 | (result, error) = pr.communicate() 347 | --------------------------------------------------------------------------------