├── .github └── workflows │ └── vim.yml ├── .gitignore ├── LICENSE ├── README.md ├── autoload ├── coloredit.vim └── coloredit │ ├── converter.vim │ └── parser.vim ├── coloredit.gif ├── plugin └── coloredit.vim └── syntax └── coloredit.vim /.github/workflows/vim.yml: -------------------------------------------------------------------------------- 1 | name: vim 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: rhysd/action-setup-vim@v1 13 | with: 14 | version: nightly 15 | - name: Run unit tests 16 | run: | 17 | vim --version 18 | vim -u NONE -N --noplugin -c "set rtp+=." -c "call coloredit#converter#run_tests()" -c "qa!" 19 | if test -f test.log; then exit 1; fi 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | Thumbs.db 3 | .ls-files 4 | tags 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Naruhiko Nishino 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # vim-coloredit 3 | 4 | This plugin provides to edit RGB and HSL such as the following: 5 | 6 | * `hsl({hue},{saturation}%,{lightness}%)` 7 | * `hsla({hue},{saturation}%,{lightness}%,{alpha})` 8 | * `rgb({red},{green},{blue})` 9 | * `rgba({red},{green},{blue},{alpha})` 10 | * `#rrggbb` 11 | 12 | When you edit `rgba()` or `hsla()`, this plugin does not support to edit the alpha value. 13 | 14 | Also this plugin provides to switch to another display-mode such as RGB and HSL. 15 | 16 | ## Requirements 17 | 18 | * Vim only. Does not support Neovim 19 | * Vim must be compiled with `+popupwin` or `+textprop` feature 20 | * 256 colors(`has('gui_running')` or `&termguicolors`) 21 | 22 | ## Concepts 23 | 24 | * This plugin does not provide to customize user-settings. 25 | * This plugin provides only one command. 26 | 27 | ## Installation 28 | 29 | This is an example of installation using [vim-plug](https://github.com/junegunn/vim-plug). 30 | 31 | ``` 32 | Plug 'rbtnn/vim-coloredit' 33 | ``` 34 | 35 | ## Usage 36 | 37 | ### :ColorEdit 38 | 39 | ![](https://raw.githubusercontent.com/rbtnn/vim-coloredit/master/coloredit.gif) 40 | 41 | ## License 42 | 43 | Distributed under MIT License. See LICENSE. 44 | -------------------------------------------------------------------------------- /autoload/coloredit.vim: -------------------------------------------------------------------------------- 1 | 2 | let s:pluginname = 'ColorEdit' 3 | let s:delimiter = '|' 4 | let s:info = 0 5 | 6 | function! coloredit#enabled() abort 7 | let flag = v:false 8 | if has('gui_running') 9 | let flag = v:true 10 | elseif has('termguicolors') 11 | if &termguicolors 12 | let flag = v:true 13 | endif 14 | endif 15 | return (has('popupwin') || has('textprop')) && flag 16 | endfunction 17 | 18 | function! coloredit#exec() abort 19 | if coloredit#enabled() 20 | let info = coloredit#parser#hash_rgb() 21 | if empty(info) 22 | let info = coloredit#parser#paren_rgb() 23 | endif 24 | if empty(info) 25 | let info = coloredit#parser#paren_rgba() 26 | endif 27 | if empty(info) 28 | let info = coloredit#parser#paren_hsl() 29 | endif 30 | if empty(info) 31 | let info = coloredit#parser#paren_hsla() 32 | endif 33 | if !empty(info) 34 | let options = { 35 | \ 'pos' : 'center', 36 | \ 'filter' : 'coloredit#filter', 37 | \ 'callback' : 'coloredit#callback', 38 | \ } 39 | let lines = [repeat('X', len(s:makeline_rgb('_', 0)))] 40 | if (info.type == 'hsl') || (info.type == 'hsla') 41 | let lines += [ 42 | \ s:makeline_hsl('H', info.hue), 43 | \ s:makeline_hsl('S', info.saturation), 44 | \ s:makeline_hsl('L', info.lightness), 45 | \ ] 46 | else 47 | let lines += [ 48 | \ s:makeline_rgb('R', info.red), 49 | \ s:makeline_rgb('G', info.green), 50 | \ s:makeline_rgb('B', info.blue), 51 | \ ] 52 | endif 53 | let lines += (empty(info.alpha) ? [''] : [(s:makeline_alpha('A', info.alpha))]) 54 | if (info.type == 'hsl') || (info.type == 'hsla') 55 | let lines += ['display-mode:HSL -> RGB'] 56 | else 57 | let lines += ['display-mode:RGB -> HSL'] 58 | endif 59 | let winid = popup_menu(lines, options) 60 | if (info.type == 'hsl') || (info.type == 'hsla') 61 | call coloredit#set_color_on_firstline_hsl(winid) 62 | else 63 | call coloredit#set_color_on_firstline_rgb(winid) 64 | endif 65 | call win_execute(winid, printf('call setpos(".", [0, %d, 1, 0])', 2)) 66 | call win_execute(winid, 'setfiletype coloredit') 67 | let s:info = info 68 | else 69 | echohl Error 70 | echo printf('[%s] please position the cursor on RGB or HSL', s:pluginname) 71 | echohl None 72 | endif 73 | else 74 | echohl Error 75 | echo printf('[%s] does not support your vim', s:pluginname) 76 | echohl None 77 | endif 78 | endfunction 79 | 80 | function! s:get_3or4values(winid) abort 81 | let lines = getbufline(winbufnr(a:winid), 1, '$') 82 | let xs = [ 83 | \ str2nr(get(split(lines[1], s:delimiter), 1, '0')), 84 | \ str2nr(get(split(lines[2], s:delimiter), 1, '0')), 85 | \ str2nr(get(split(lines[3], s:delimiter), 1, '0')) 86 | \ ] 87 | if 4 < len(lines) 88 | let alpha = split(lines[4], s:delimiter) 89 | if get(alpha, 0, '') == 'A' 90 | let xs += [get(alpha, 1, '0')] 91 | endif 92 | endif 93 | return xs 94 | endfunction 95 | 96 | function! coloredit#generate_rgb(winid, is_paren) abort 97 | if a:is_paren 98 | return call('printf', ['rgb(%d, %d, %d)'] + s:get_3or4values(a:winid)[:2]) 99 | else 100 | return call('printf', ['#%02x%02x%02x'] + s:get_3or4values(a:winid)[:2]) 101 | endif 102 | endfunction 103 | 104 | function! coloredit#generate_rgba(winid, is_paren) abort 105 | return call('printf', ['rgba(%d, %d, %d, %s)'] + s:get_3or4values(a:winid)) 106 | endfunction 107 | 108 | function! coloredit#generate_hsl(winid, is_paren) abort 109 | return call('printf', ['hsl(%d, %d%%, %d%%)'] + s:get_3or4values(a:winid)[:2]) 110 | endfunction 111 | 112 | function! coloredit#generate_hsla(winid, is_paren) abort 113 | return call('printf', ['hsla(%d, %d%%, %d%%, %s)'] + s:get_3or4values(a:winid)) 114 | endfunction 115 | 116 | function! coloredit#generate_hash_rgb_from_hsl(winid) abort 117 | return call ('printf', ['#%02x%02x%02x'] + call('coloredit#converter#hsl2rgb', s:get_3or4values(a:winid)[:2])) 118 | endfunction 119 | 120 | function! coloredit#set_color_on_firstline_rgb(winid) abort 121 | let rgb = coloredit#generate_rgb(a:winid, v:false) 122 | call win_execute(a:winid, printf('highlight! %sFirstLine guifg=%s guibg=%s', s:pluginname, rgb, rgb)) 123 | endfunction 124 | 125 | function! coloredit#set_color_on_firstline_hsl(winid) abort 126 | let rgb = coloredit#generate_hash_rgb_from_hsl(a:winid) 127 | call win_execute(a:winid, printf('highlight! %sFirstLine guifg=%s guibg=%s', s:pluginname, rgb, rgb)) 128 | endfunction 129 | 130 | function! coloredit#callback(winid, key) abort 131 | if -1 != a:key 132 | let bnr = winbufnr(a:winid) 133 | let lines = getbufline(bnr, 1, '$') 134 | let is_rbg = lines[1][0] == 'R' 135 | if s:info.type == 'rgb' 136 | if !is_rbg 137 | let rgb = call('coloredit#converter#hsl2rgb', s:get_3or4values(a:winid)[:2]) 138 | call s:setline_rgb(bnr, 2, 'R', rgb[0]) 139 | call s:setline_rgb(bnr, 3, 'G', rgb[1]) 140 | call s:setline_rgb(bnr, 4, 'B', rgb[2]) 141 | endif 142 | call setline('.', s:info.head .. coloredit#generate_rgb(a:winid, s:info.is_paren) .. s:info.tail) 143 | elseif s:info.type == 'rgba' 144 | if !is_rbg 145 | let rgb = call('coloredit#converter#hsl2rgb', s:get_3or4values(a:winid)[:2]) 146 | call s:setline_rgb(bnr, 2, 'R', rgb[0]) 147 | call s:setline_rgb(bnr, 3, 'G', rgb[1]) 148 | call s:setline_rgb(bnr, 4, 'B', rgb[2]) 149 | endif 150 | call setline('.', s:info.head .. coloredit#generate_rgba(a:winid, s:info.is_paren) .. s:info.tail) 151 | elseif s:info.type == 'hsl' 152 | if is_rbg 153 | let hsl = call('coloredit#converter#rgb2hsl_nr', s:get_3or4values(a:winid)[:2]) 154 | call s:setline_rgb(bnr, 2, 'H', hsl[0]) 155 | call s:setline_rgb(bnr, 3, 'S', hsl[1]) 156 | call s:setline_rgb(bnr, 4, 'L', hsl[2]) 157 | endif 158 | call setline('.', s:info.head .. coloredit#generate_hsl(a:winid, s:info.is_paren) .. s:info.tail) 159 | elseif s:info.type == 'hsla' 160 | if is_rbg 161 | let hsl = call('coloredit#converter#rgb2hsl_nr', s:get_3or4values(a:winid)[:2]) 162 | call s:setline_rgb(bnr, 2, 'H', hsl[0]) 163 | call s:setline_rgb(bnr, 3, 'S', hsl[1]) 164 | call s:setline_rgb(bnr, 4, 'L', hsl[2]) 165 | endif 166 | call setline('.', s:info.head .. coloredit#generate_hsla(a:winid, s:info.is_paren) .. s:info.tail) 167 | endif 168 | else 169 | echohl Error 170 | echo printf('[%s] cancel', s:pluginname) 171 | echohl None 172 | endif 173 | endfunction 174 | 175 | function! coloredit#filter(winid, key) abort 176 | call win_execute(a:winid, 'let w:lnum = line(".")') 177 | let lnum = getwinvar(a:winid, 'lnum', 2) 178 | let bnr = winbufnr(a:winid) 179 | let lines = getbufline(bnr, 1, '$') 180 | let is_rbg = lines[1][0] == 'R' 181 | if a:key == 'j' 182 | let lnum += 1 183 | if (1 < lnum) && (lnum <= 4) 184 | call setwinvar(a:winid, 'lnum', lnum) 185 | elseif lnum == 5 186 | let lnum += 1 187 | call win_execute(a:winid, printf('call setpos(".", [0, %d, 1, 0])', lnum)) 188 | call setwinvar(a:winid, 'lnum', lnum) 189 | return 1 190 | else 191 | return 1 192 | endif 193 | endif 194 | if a:key == 'k' 195 | let lnum -= 1 196 | if (1 < lnum) && (lnum <= 4) 197 | call setwinvar(a:winid, 'lnum', lnum) 198 | elseif lnum == 5 199 | let lnum -= 1 200 | call win_execute(a:winid, printf('call setpos(".", [0, %d, 1, 0])', lnum)) 201 | call setwinvar(a:winid, 'lnum', lnum) 202 | return 1 203 | else 204 | return 1 205 | endif 206 | endif 207 | if (a:key == 'h') && (1 < lnum) && (lnum <= 4) 208 | let xs = split(lines[lnum - 1], s:delimiter) 209 | let n = str2nr(xs[1]) - 1 210 | if is_rbg 211 | call s:setline_rgb(bnr, lnum, xs[0], n) 212 | call coloredit#set_color_on_firstline_rgb(a:winid) 213 | else 214 | call s:setline_hsl(bnr, lnum, xs[0], n) 215 | call coloredit#set_color_on_firstline_hsl(a:winid) 216 | endif 217 | return 1 218 | endif 219 | if (a:key == 'l') && (1 < lnum) && (lnum <= 4) 220 | let xs = split(lines[lnum - 1], s:delimiter) 221 | let n = str2nr(xs[1]) + 1 222 | if is_rbg 223 | call s:setline_rgb(bnr, lnum, xs[0], n) 224 | call coloredit#set_color_on_firstline_rgb(a:winid) 225 | else 226 | call s:setline_hsl(bnr, lnum, xs[0], n) 227 | call coloredit#set_color_on_firstline_hsl(a:winid) 228 | endif 229 | return 1 230 | endif 231 | if ((a:key == 'h') || (a:key == 'l')) && (6 == lnum) 232 | if lines[lnum - 1] == 'display-mode:HSL -> RGB' 233 | let rgb = call('coloredit#converter#hsl2rgb', s:get_3or4values(a:winid)[:2]) 234 | call s:setline_rgb(bnr, 2, 'R', rgb[0]) 235 | call s:setline_rgb(bnr, 3, 'G', rgb[1]) 236 | call s:setline_rgb(bnr, 4, 'B', rgb[2]) 237 | call setbufline(bnr, lnum, 'display-mode:RGB -> HSL') 238 | call coloredit#set_color_on_firstline_rgb(a:winid) 239 | else 240 | let hsl = call('coloredit#converter#rgb2hsl_nr', s:get_3or4values(a:winid)[:2]) 241 | call s:setline_hsl(bnr, 2, 'H', hsl[0]) 242 | call s:setline_hsl(bnr, 3, 'S', hsl[1]) 243 | call s:setline_hsl(bnr, 4, 'L', hsl[2]) 244 | call setbufline(bnr, lnum, 'display-mode:HSL -> RGB') 245 | call coloredit#set_color_on_firstline_hsl(a:winid) 246 | endif 247 | return 1 248 | endif 249 | return popup_filter_menu(a:winid, a:key) 250 | endfunction 251 | 252 | function! s:makeline_rgb(x, n) abort 253 | let n = a:n 254 | if n < 0 255 | let n = 0 256 | endif 257 | if 255 < n 258 | let n = 255 259 | endif 260 | return printf('%s%s%3d%s%-25s', a:x, s:delimiter, n, s:delimiter, repeat('=', n / 10)) 261 | endfunction 262 | 263 | function! s:makeline_hsl(x, n) abort 264 | let n = a:n 265 | if n < 0 266 | let n = 0 267 | endif 268 | if a:x == 'H' 269 | if 360 < n 270 | let n = 360 271 | endif 272 | return printf('%s%s%3d%s%-25s', a:x, s:delimiter, n, s:delimiter, repeat('=', (n * 255 / 360 / 10))) 273 | else 274 | if 100 < n 275 | let n = 100 276 | endif 277 | return printf('%s%s%3d%s%-25s', a:x, s:delimiter, n, s:delimiter, repeat('=', (n * 255 / 100 / 10))) 278 | endif 279 | endfunction 280 | 281 | function! s:makeline_alpha(x, n) abort 282 | return printf('%s%s%s', a:x, s:delimiter, a:n) 283 | endfunction 284 | 285 | function! s:setline_rgb(bnr, lnum, x, n) abort 286 | call setbufline(a:bnr, a:lnum, s:makeline_rgb(a:x, a:n)) 287 | endfunction 288 | 289 | function! s:setline_hsl(bnr, lnum, x, n) abort 290 | call setbufline(a:bnr, a:lnum, s:makeline_hsl(a:x, a:n)) 291 | endfunction 292 | 293 | -------------------------------------------------------------------------------- /autoload/coloredit/converter.vim: -------------------------------------------------------------------------------- 1 | 2 | let s:TEST_LOG = expand(':h:h:gs?\?/?') . '/test.log' 3 | 4 | function! coloredit#converter#hsl2rgb(H, S, L) abort 5 | if a:L <= 49 6 | let max = 2.55 * (a:L + a:L * (1.0 * a:S / 100)) 7 | let min = 2.55 * (a:L - a:L * (1.0 * a:S / 100)) 8 | else 9 | let max = 2.55 * (a:L + (100 - a:L) * (1.0 * a:S / 100)) 10 | let min = 2.55 * (a:L - (100 - a:L) * (1.0 * a:S / 100)) 11 | endif 12 | if (0 <= a:H) && (a:H < 60) 13 | let R = max 14 | let G = (1.0 * a:H / 60) * (max - min) + min 15 | let B = min 16 | elseif (60 <= a:H) && (a:H < 120) 17 | let R = (1.0 * (120 - a:H) / 60) * (max - min) + min 18 | let G = max 19 | let B = min 20 | elseif (120 <= a:H) && (a:H < 180) 21 | let R = min 22 | let G = max 23 | let B = (1.0 * (a:H - 120) / 60) * (max - min) + min 24 | elseif (180 <= a:H) && (a:H < 240) 25 | let R = min 26 | let G = (1.0 * (240 - a:H) / 60) * (max - min) + min 27 | let B = max 28 | elseif (240 <= a:H) && (a:H < 300) 29 | let R = (1.0 * (a:H - 240) / 60) * (max - min) + min 30 | let G = min 31 | let B = max 32 | elseif (300 <= a:H) && (a:H <= 360) 33 | let R = max 34 | let G = min 35 | let B = (1.0 * (360 - a:H) / 60) * (max - min) + min 36 | endif 37 | return map([R, G, B], { i,x -> float2nr(ceil(x)) }) 38 | endfunction 39 | 40 | " NOTE: https://www.rapidtables.com/convert/color/rgb-to-hsl.html 41 | " This returns HSL float value that up to the first place in the minority. 42 | function! coloredit#converter#rgb2hsl_float(R, G, B) abort 43 | let max = max([(a:R), (a:G), (a:B)]) 44 | let min = min([(a:R), (a:G), (a:B)]) 45 | 46 | " Hue calculation 47 | if max == min 48 | let H = 0 49 | elseif max == a:R 50 | let H = 60 * (1.0 * (a:G - a:B) / (max - min)) 51 | elseif max == a:G 52 | let H = 60 * (1.0 * (a:B - a:R) / (max - min)) + 120 53 | elseif max == a:B 54 | let H = 60 * (1.0 * (a:R - a:G) / (max - min)) + 240 55 | else 56 | let H = 0 57 | endif 58 | if H < 0 59 | let H = H + 360 60 | endif 61 | 62 | " Saturation calculation 63 | if max == min 64 | let S = 0 65 | else 66 | if 128 <= (max + min) / 2 67 | let S = 1.0 * (max - min) / (510 - max - min) 68 | else 69 | let S = 1.0 * (max - min) / (max + min) 70 | endif 71 | let S *= 100 72 | endif 73 | 74 | " Lightness calculation 75 | let L = 1.0 * (max + min) / 2 / 255 76 | let L *= 100 77 | 78 | return map([H, S, L], { i,x -> floor(x * 10) / 10 }) 79 | endfunction 80 | 81 | " This returns HSL real number value 82 | function! coloredit#converter#rgb2hsl_nr(R, G, B) abort 83 | let [H, S, L] = coloredit#converter#rgb2hsl_float(a:R, a:G, a:B) 84 | return map([H, S, L], { i,x -> float2nr(ceil(x)) }) 85 | endfunction 86 | 87 | function! coloredit#converter#run_tests() abort 88 | if filereadable(s:TEST_LOG) 89 | call delete(s:TEST_LOG) 90 | endif 91 | 92 | let v:errors = [] 93 | 94 | call assert_equal( 95 | \ [[60, 34, 30], [60.0, 33.3, 30.0]], 96 | \ [coloredit#converter#rgb2hsl_nr(0x66, 0x66, 0x33), 97 | \ coloredit#converter#rgb2hsl_float(0x66, 0x66, 0x33) 98 | \ ]) 99 | call assert_equal( 100 | \ [[207, 82, 66], [207.0, 81.6, 65.8]], 101 | \ [coloredit#converter#rgb2hsl_nr(0x61, 0xaf, 0xef), 102 | \ coloredit#converter#rgb2hsl_float(0x61, 0xaf, 0xef) 103 | \ ]) 104 | " Black 105 | call assert_equal( 106 | \ [[0, 0, 0], [0.0, 0.0, 0.0]], 107 | \ [coloredit#converter#rgb2hsl_nr(0x00, 0x00, 0x00), 108 | \ coloredit#converter#rgb2hsl_float(0x00, 0x00, 0x00) 109 | \ ]) 110 | " White 111 | call assert_equal( 112 | \ [[0, 0, 100], [0.0, 0.0, 100.0]], 113 | \ [coloredit#converter#rgb2hsl_nr(0xff, 0xff, 0xff), 114 | \ coloredit#converter#rgb2hsl_float(0xff, 0xff, 0xff) 115 | \ ]) 116 | " Red 117 | call assert_equal( 118 | \ [[0, 100, 50], [0.0, 100.0, 50.0]], 119 | \ [coloredit#converter#rgb2hsl_nr(0xff, 0x00, 0x00), 120 | \ coloredit#converter#rgb2hsl_float(0xff, 0x00, 0x00) 121 | \ ]) 122 | " Lime 123 | call assert_equal( 124 | \ [[120, 100, 50], [120.0, 100.0, 50.0]], 125 | \ [coloredit#converter#rgb2hsl_nr(0x00, 0xff, 0x00), 126 | \ coloredit#converter#rgb2hsl_float(0x00, 0xff, 0x00) 127 | \ ]) 128 | " Blue 129 | call assert_equal( 130 | \ [[240, 100, 50], [240.0, 100.0, 50.0]], 131 | \ [coloredit#converter#rgb2hsl_nr(0x00, 0x00, 0xff), 132 | \ coloredit#converter#rgb2hsl_float(0x00, 0x00, 0xff) 133 | \ ]) 134 | " Yellow 135 | call assert_equal( 136 | \ [[60, 100, 50], [60.0, 100.0, 50.0]], 137 | \ [coloredit#converter#rgb2hsl_nr(0xff, 0xff, 0x00), 138 | \ coloredit#converter#rgb2hsl_float(0xff, 0xff, 0x00) 139 | \ ]) 140 | " Cyan 141 | call assert_equal( 142 | \ [[180, 100, 50], [180.0, 100.0, 50.0]], 143 | \ [coloredit#converter#rgb2hsl_nr(0x00, 0xff, 0xff), 144 | \ coloredit#converter#rgb2hsl_float(0x00, 0xff, 0xff) 145 | \ ]) 146 | " Magenta 147 | call assert_equal( 148 | \ [[300, 100, 50], [300.0, 100.0, 50.0]], 149 | \ [coloredit#converter#rgb2hsl_nr(0xff, 0x00, 0xff), 150 | \ coloredit#converter#rgb2hsl_float(0xff, 0x00, 0xff) 151 | \ ]) 152 | " Silver 153 | call assert_equal( 154 | \ [[0, 0, 75], [0.0, 0.0, 74.9]], 155 | \ [coloredit#converter#rgb2hsl_nr(0xbf, 0xbf, 0xbf), 156 | \ coloredit#converter#rgb2hsl_float(0xbf, 0xbf, 0xbf) 157 | \ ]) 158 | " Gray 159 | call assert_equal( 160 | \ [[0, 0, 51], [0.0, 0.0, 50.1]], 161 | \ [coloredit#converter#rgb2hsl_nr(0x80, 0x80, 0x80), 162 | \ coloredit#converter#rgb2hsl_float(0x80, 0x80, 0x80) 163 | \ ]) 164 | " Maroon 165 | call assert_equal( 166 | \ [[0, 100, 25], [0.0, 100.0, 25.0]], 167 | \ [coloredit#converter#rgb2hsl_nr(0x80, 0x00, 0x00), 168 | \ coloredit#converter#rgb2hsl_float(0x80, 0x00, 0x00) 169 | \ ]) 170 | " Olive 171 | call assert_equal( 172 | \ [[60, 100, 25], [60.0, 100.0, 25.0]], 173 | \ [coloredit#converter#rgb2hsl_nr(0x80, 0x80, 0x00), 174 | \ coloredit#converter#rgb2hsl_float(0x80, 0x80, 0x00) 175 | \ ]) 176 | " Green 177 | call assert_equal( 178 | \ [[120, 100, 25], [120.0, 100.0, 25.0]], 179 | \ [coloredit#converter#rgb2hsl_nr(0x00, 0x80, 0x00), 180 | \ coloredit#converter#rgb2hsl_float(0x00, 0x80, 0x00) 181 | \ ]) 182 | " Purple 183 | call assert_equal( 184 | \ [[300, 100, 25], [300.0, 100.0, 25.0]], 185 | \ [coloredit#converter#rgb2hsl_nr(0x80, 0x00, 0x80), 186 | \ coloredit#converter#rgb2hsl_float(0x80, 0x00, 0x80) 187 | \ ]) 188 | " Teal 189 | call assert_equal( 190 | \ [[180, 100, 25], [180.0, 100.0, 25.0]], 191 | \ [coloredit#converter#rgb2hsl_nr(0x00, 0x80, 0x80), 192 | \ coloredit#converter#rgb2hsl_float(0x00, 0x80, 0x80) 193 | \ ]) 194 | " Navy 195 | call assert_equal( 196 | \ [[240, 100, 25], [240.0, 100.0, 25.0]], 197 | \ [coloredit#converter#rgb2hsl_nr(0x00, 0x00, 0x80), 198 | \ coloredit#converter#rgb2hsl_float(0x00, 0x00, 0x80) 199 | \ ]) 200 | 201 | if !empty(v:errors) 202 | call writefile(v:errors, s:TEST_LOG) 203 | for err in v:errors 204 | echohl Error 205 | echo substitute(substitute(err, 'Expected ', "\n \&", ''), 'but got ', "\n \& ", '') 206 | echohl None 207 | endfor 208 | endif 209 | endfunction 210 | 211 | call coloredit#converter#run_tests() 212 | -------------------------------------------------------------------------------- /autoload/coloredit/parser.vim: -------------------------------------------------------------------------------- 1 | 2 | function! coloredit#parser#hash_rgb() abort 3 | let cs = split(getline('.'), '\zs') 4 | let i = s:col2idx(cs, col('.')) 5 | let s = i 6 | let e = i 7 | if 0 < len(cs) 8 | if cs[i] =~# '^[#a-fA-F0-9]$' 9 | while 0 <= s - 1 10 | if cs[s - 1] =~# '^[#a-fA-F0-9]$' 11 | let s -= 1 12 | else 13 | break 14 | endif 15 | endwhile 16 | while e + 1 < len(cs) 17 | if cs[e + 1] =~# '^[#a-fA-F0-9]$' 18 | let e += 1 19 | else 20 | break 21 | endif 22 | endwhile 23 | let ss = s:split3(cs, s, e) 24 | let regex = '^#\([a-fA-F0-9][a-fA-F0-9]\)\([a-fA-F0-9][a-fA-F0-9]\)\([a-fA-F0-9][a-fA-F0-9]\)$' 25 | let m = matchlist(ss[1], regex) 26 | if !empty(m) 27 | return { 28 | \ 'type' : 'rgb', 29 | \ 'is_paren' : v:false, 30 | \ 'head' : ss[0], 31 | \ 'tail' : ss[2], 32 | \ 'red' : str2nr(m[1], 16), 33 | \ 'green' : str2nr(m[2], 16), 34 | \ 'blue' : str2nr(m[3], 16), 35 | \ 'alpha' : '', 36 | \ } 37 | endif 38 | endif 39 | endif 40 | return {} 41 | endfunction 42 | 43 | function! coloredit#parser#paren_rgb() abort 44 | let cs = split(getline('.'), '\zs') 45 | let i = s:col2idx(cs, col('.')) 46 | let s = i 47 | let e = i 48 | if 0 < len(cs) 49 | while 0 <= s 50 | if (cs[s] == 'r') || (cs[s] == 'R') 51 | break 52 | else 53 | let s -= 1 54 | endif 55 | endwhile 56 | while e < len(cs) 57 | if cs[e] == ')' 58 | break 59 | else 60 | let e += 1 61 | endif 62 | endwhile 63 | let ss = s:split3(cs, s, e) 64 | let regex = '^[rR][gG][bB](\s*\([0-9]\+\)\s*,\s*\([0-9]\+\)\s*,\s*\([0-9]\+\)\s*)$' 65 | let m = matchlist(ss[1], regex) 66 | if !empty(m) 67 | return { 68 | \ 'type' : 'rgb', 69 | \ 'is_paren' : v:true, 70 | \ 'head' : ss[0], 71 | \ 'tail' : ss[2], 72 | \ 'red' : str2nr(m[1], 10), 73 | \ 'green' : str2nr(m[2], 10), 74 | \ 'blue' : str2nr(m[3], 10), 75 | \ 'alpha' : '', 76 | \ } 77 | endif 78 | endif 79 | return {} 80 | endfunction 81 | 82 | function! coloredit#parser#paren_rgba() abort 83 | let cs = split(getline('.'), '\zs') 84 | let i = s:col2idx(cs, col('.')) 85 | let s = i 86 | let e = i 87 | if 0 < len(cs) 88 | while 0 <= s 89 | if (cs[s] == 'r') || (cs[s] == 'R') 90 | break 91 | else 92 | let s -= 1 93 | endif 94 | endwhile 95 | while e < len(cs) 96 | if cs[e] == ')' 97 | break 98 | else 99 | let e += 1 100 | endif 101 | endwhile 102 | let ss = s:split3(cs, s, e) 103 | let regex = '^[rR][gG][bB][aA](\s*\([0-9]\+\)\s*,\s*\([0-9]\+\)\s*,\s*\([0-9]\+\)\s*,\s*\([0-9.]\+\)\s*)$' 104 | let m = matchlist(ss[1], regex) 105 | if !empty(m) 106 | return { 107 | \ 'type' : 'rgba', 108 | \ 'is_paren' : v:true, 109 | \ 'head' : ss[0], 110 | \ 'tail' : ss[2], 111 | \ 'red' : str2nr(m[1], 10), 112 | \ 'green' : str2nr(m[2], 10), 113 | \ 'blue' : str2nr(m[3], 10), 114 | \ 'alpha' : m[4], 115 | \ } 116 | endif 117 | endif 118 | return {} 119 | endfunction 120 | 121 | function! coloredit#parser#paren_hsl() abort 122 | let cs = split(getline('.'), '\zs') 123 | let i = s:col2idx(cs, col('.')) 124 | let s = i 125 | let e = i 126 | if 0 < len(cs) 127 | while 0 <= s 128 | if (cs[s] == 'h') || (cs[s] == 'H') 129 | break 130 | else 131 | let s -= 1 132 | endif 133 | endwhile 134 | while e < len(cs) 135 | if cs[e] == ')' 136 | break 137 | else 138 | let e += 1 139 | endif 140 | endwhile 141 | let ss = s:split3(cs, s, e) 142 | let regex = '^[hH][sS][lL](\s*\([0-9]\+\)\s*,\s*\([0-9]\+\)\s*%,\s*\([0-9]\+\)\s*%)$' 143 | let m = matchlist(ss[1], regex) 144 | if !empty(m) 145 | return { 146 | \ 'type' : 'hsl', 147 | \ 'is_paren' : v:true, 148 | \ 'head' : ss[0], 149 | \ 'tail' : ss[2], 150 | \ 'hue' : str2nr(m[1], 10), 151 | \ 'saturation' : str2nr(m[2], 10), 152 | \ 'lightness' : str2nr(m[3], 10), 153 | \ 'alpha' : '', 154 | \ } 155 | endif 156 | endif 157 | return {} 158 | endfunction 159 | 160 | function! coloredit#parser#paren_hsla() abort 161 | let cs = split(getline('.'), '\zs') 162 | let i = s:col2idx(cs, col('.')) 163 | let s = i 164 | let e = i 165 | if 0 < len(cs) 166 | while 0 <= s 167 | if (cs[s] == 'h') || (cs[s] == 'H') 168 | break 169 | else 170 | let s -= 1 171 | endif 172 | endwhile 173 | while e < len(cs) 174 | if cs[e] == ')' 175 | break 176 | else 177 | let e += 1 178 | endif 179 | endwhile 180 | let ss = s:split3(cs, s, e) 181 | let regex = '^[hH][sS][lL][aA](\s*\([0-9]\+\)\s*,\s*\([0-9]\+\)\s*%,\s*\([0-9]\+\)\s*%,\s*\([0-9.]\+\)\s*)$' 182 | let m = matchlist(ss[1], regex) 183 | if !empty(m) 184 | return { 185 | \ 'type' : 'hsla', 186 | \ 'is_paren' : v:true, 187 | \ 'head' : ss[0], 188 | \ 'tail' : ss[2], 189 | \ 'hue' : str2nr(m[1], 10), 190 | \ 'saturation' : str2nr(m[2], 10), 191 | \ 'lightness' : str2nr(m[3], 10), 192 | \ 'alpha' : m[4], 193 | \ } 194 | endif 195 | endif 196 | return {} 197 | endfunction 198 | 199 | function! s:col2idx(cs, col) abort 200 | let i = 0 201 | let total = a:col 202 | for c in a:cs 203 | let total -= len(c) 204 | if 0 < total 205 | let i += 1 206 | else 207 | break 208 | endif 209 | endfor 210 | return i 211 | endfunction 212 | 213 | function! s:split3(cs, s, e) abort 214 | let cs = a:cs 215 | let s = a:s 216 | let e = a:e 217 | let s1 = (0 < a:s) ? join(cs[:(s - 1)], '') : '' 218 | let s2 = join(cs[(s):(e)], '') 219 | let s3 = join(cs[(e + 1):], '') 220 | return [s1, s2, s3] 221 | endfunction 222 | 223 | -------------------------------------------------------------------------------- /coloredit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbtnn/vim-coloredit/eda0cc2f7f7605305e549c7408cd177958d2e387/coloredit.gif -------------------------------------------------------------------------------- /plugin/coloredit.vim: -------------------------------------------------------------------------------- 1 | 2 | let g:loaded_coloredit = 1 3 | 4 | command! -nargs=0 ColorEdit :call coloredit#exec() 5 | 6 | -------------------------------------------------------------------------------- /syntax/coloredit.vim: -------------------------------------------------------------------------------- 1 | 2 | if exists("b:current_syntax") 3 | finish 4 | endif 5 | 6 | syntax match ColorEditFirstLine 'X' 7 | 8 | --------------------------------------------------------------------------------