i3-style
Version:
Make your i3wm config a little more stylish
193 lines (160 loc) • 5.72 kB
text/coffeescript
_ = require 'underscore'
namer = require 'color-namer'
# change to mustache style templates
_.templateSettings = interpolate :/\{\{(.+?)\}\}/g
# templates to build lines in the config
templates =
windowColors: _.template "client.{{state}} {{border}} {{background}} {{text}} {{indicator}}"
barColors: _.template "{{state}} {{border}} {{background}} {{text}}"
# matches the bar directive and captures the inside
# IMPORTANT: only works if the bar doesn't have colors directive! strip first!
# TODO: fix this so it will ignore inner braces
matchBar = ///
bar\s*{\s*\n
([^}]*)
\n}
///g
# matches a colors directive, such as may be inside a bar directive
matchBarColors = ///
\s*colors\s*{
[^}]*
}
///g
# matches a client color directive
matchWindowColors = ///
\s*client\.
(?:focused | focused_inactive | unfocused | urgent)
\s+
.*
///g
# strips the config of any (bar) color directives and client color directives
stripConfig = (config) ->
config.replace(matchWindowColors, '').replace(matchBarColors, '')
# takes color aliases and window_color directive object and returns i3 config
# client color directives string
mkWindowColors = (colors = {}, windowColors) ->
result = []
_.each windowColors, (parts, state) ->
result.push templates.windowColors
state: state
border: colors[parts.border] or parts.border
background: colors[parts.background] or parts.background
text: colors[parts.text] or parts.text
indicator: colors[parts.indicator] or parts.indicator
return result.join '\n'
# takes color aliases and bar color directive object and returns i3 config bar
# color directive string
mkBarColors = (colors = {}, bar_colors) ->
result = []
_.each bar_colors, (parts, state) ->
if _.isString parts
result.push "#{state} #{colors[parts] or parts}"
else
result.push templates.barColors
state: state
border: colors[parts.border] or parts.border
background: colors[parts.background] or parts.background
text: colors[parts.text] or parts.text
return result.join "\n "
# takes theme object and i3 config string and returns a new i3 config string
# with the theme applied
mkConfig = (theme, config) ->
config = stripConfig config
config = config.replace matchBar, (match, inside) ->
return """
bar {
#{inside.trim()}
colors {
#{mkBarColors theme.colors, theme.bar_colors}
}
}
"""
config += "\n#{mkWindowColors theme.colors, theme.window_colors}\n"
return config
# takes a config and returns an i3-style theme
mkTheme = (config) ->
theme =
meta:
description: 'AUTOMATICALLY GENERATED THEME'
# add a color name to the theme for the given hex color and return the name
# of the color
addColor = (hex) ->
if not hex
return null
unless theme.colors?
theme.colors = {}
colorName = namer(hex).html[0].name
if theme.colors[colorName]? and theme.colors[colorName] isnt hex
i = 0
while true
i += 1
c = "#{colorName}#{i}"
if not theme.colors[c] or theme.colors[c] == hex
colorName = c
break
theme.colors[colorName] = hex
return colorName
stateBarInside = off
stateColorsInside = off
for line in config.split('\n')
# trim the line
line = line.replace(/^\s+|\s+$/g, '')
# remove duplicate spaces between words
line = line.replace(/\s+/g, ' ')
# beginning of a bar block
if line.indexOf('bar {') is 0
stateBarInside = on
continue
# beginning of a bar color block
if line.indexOf('colors {') is 0 and stateBarInside
stateColorsInside = on
continue
if line.indexOf('}') is 0
if stateColorsInside
# end of a bar color block
stateColorsInside = off
continue
if stateBarInside
# end of a bar block
stateBarInside = off
continue
# check for window color config directive
if line.indexOf('client.') == 0
color = line.split(' ')
color[0] = color[0].substring('client.'.length)
if color[0] in ['focused', 'focused_inactive', 'unfocused', 'urgent']
border = addColor color[1]
background = addColor color[2]
text = addColor color[3]
indicator = addColor color[4]
unless theme.window_colors?
theme.window_colors = {}
theme.window_colors[color[0]] = {}
if border? then theme.window_colors[color[0]].border = border
if background? then theme.window_colors[color[0]].background = background
if text? then theme.window_colors[color[0]].text = text
if indicator? then theme.window_colors[color[0]].indicator = indicator
continue
# check for bar color config directive
if stateColorsInside
barColor = line.split(' ')
if barColor[0] in ['separator', 'background', 'statusline']
unless theme.bar_colors?
theme.bar_colors = {}
theme.bar_colors[barColor[0]] = addColor barColor[1]
else if barColor[0] in ['focused_workspace', 'active_workspace',
'inactive_workspace', 'urgent_workspace']
border = addColor barColor[1]
background = addColor barColor[2]
text = addColor barColor[3]
unless theme.bar_colors?
theme.bar_colors = {}
theme.bar_colors[barColor[0]] = {}
if border? then theme.bar_colors[barColor[0]].border = border
if background? then theme.bar_colors[barColor[0]].background = background
if text? then theme.bar_colors[barColor[0]].text = text
continue
return theme
module.exports =
mkConfig: mkConfig
mkTheme: mkTheme