planter-cli 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +9 -0
- data/.gitignore +44 -0
- data/.irbrc +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +78 -0
- data/.travis.yml +7 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +45 -0
- data/Gemfile +6 -0
- data/Guardfile +25 -0
- data/LICENSE.txt +20 -0
- data/README.md +208 -0
- data/Rakefile +132 -0
- data/bin/plant +106 -0
- data/debug.log +0 -0
- data/docker/Dockerfile +12 -0
- data/docker/Dockerfile-2.6 +12 -0
- data/docker/Dockerfile-2.7 +12 -0
- data/docker/Dockerfile-3.0 +11 -0
- data/docker/bash_profile +15 -0
- data/docker/inputrc +57 -0
- data/lib/.rubocop.yml +1 -0
- data/lib/planter/array.rb +28 -0
- data/lib/planter/color.rb +370 -0
- data/lib/planter/errors.rb +59 -0
- data/lib/planter/file.rb +11 -0
- data/lib/planter/fileentry.rb +87 -0
- data/lib/planter/filelist.rb +144 -0
- data/lib/planter/hash.rb +103 -0
- data/lib/planter/plant.rb +228 -0
- data/lib/planter/prompt.rb +352 -0
- data/lib/planter/script.rb +59 -0
- data/lib/planter/string.rb +383 -0
- data/lib/planter/symbol.rb +28 -0
- data/lib/planter/version.rb +7 -0
- data/lib/planter.rb +222 -0
- data/planter-cli.gemspec +48 -0
- data/scripts/deploy.rb +97 -0
- data/scripts/runtests.sh +5 -0
- data/spec/.rubocop.yml +4 -0
- data/spec/planter/plant_spec.rb +14 -0
- data/spec/planter/string_spec.rb +20 -0
- data/spec/spec_helper.rb +20 -0
- data/src/_README.md +214 -0
- metadata +400 -0
data/bin/plant
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$VERBOSE = true
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require_relative '../lib/planter'
|
8
|
+
|
9
|
+
options = {
|
10
|
+
help: false,
|
11
|
+
debug: false,
|
12
|
+
version: false
|
13
|
+
}
|
14
|
+
|
15
|
+
# Variable definition
|
16
|
+
# variables:
|
17
|
+
# - key: var
|
18
|
+
# prompt: Variable
|
19
|
+
# type: [string,float,integer,number,date]
|
20
|
+
# value: (for date type can be today, time, now, etc.)
|
21
|
+
# default: Untitled
|
22
|
+
# min: 1
|
23
|
+
# max: 5
|
24
|
+
Planter.variables = {}
|
25
|
+
Planter::Color.coloring = $stdout.isatty
|
26
|
+
|
27
|
+
opts = OptionParser.new
|
28
|
+
opts.banner = 'Usage: planter [options] TEMPLATE'
|
29
|
+
|
30
|
+
Planter.accept_defaults = false
|
31
|
+
opts.on('--defaults', 'Accept default values for all variables') do
|
32
|
+
Planter.accept_defaults = true
|
33
|
+
end
|
34
|
+
|
35
|
+
Planter.target = Dir.pwd
|
36
|
+
opts.on('-i', '--in TARGET', 'Plant in TARGET instead of current directory') do |opt|
|
37
|
+
target = File.expand_path(opt)
|
38
|
+
FileUtils.mkdir_p(target) unless File.exist?(target)
|
39
|
+
Planter.target = target
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('-k', '--var=KEY:VALUE,KEY:VALUE...', Array,
|
43
|
+
'Pass a variable on the command line as KEY:VALUE pairs. Can be used multiple times.') do |opt|
|
44
|
+
opt.each do |o|
|
45
|
+
parts = o.split(/:/)
|
46
|
+
key = parts.shift
|
47
|
+
value = parts.join(':')
|
48
|
+
Planter.variables[key.to_var] = value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on('-o', '--overwrite', 'Overwrite existing files') do
|
53
|
+
Planter.overwrite = true
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on_tail('-d', '--debug', 'Display version number') do
|
57
|
+
Planter.debug = true
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.on_tail('-h', '--help', 'Display this screen, or list variables for template argument') do
|
61
|
+
options[:help] = true
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on_tail('-v', '--version', 'Display version number') do
|
65
|
+
options[:version] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.parse!
|
69
|
+
|
70
|
+
##
|
71
|
+
## List variables for a template
|
72
|
+
##
|
73
|
+
## @param template [String] The template
|
74
|
+
##
|
75
|
+
def list_vars(template)
|
76
|
+
puts "#{template} variables:"
|
77
|
+
Planter.config = template
|
78
|
+
Planter.config[:variables].sort_by { |v| v[:key].to_var }.each do |var|
|
79
|
+
title = var[:prompt] || var[:key]
|
80
|
+
var_type = var[:type].normalize_type || :string
|
81
|
+
default = var[:value] || var[:default]
|
82
|
+
default = default ? ", default: #{default.coerce(var_type)}" : ''
|
83
|
+
puts "#{title}:"
|
84
|
+
puts " [#{var[:key].to_var}] (type: #{var_type}#{default})"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if options[:version]
|
89
|
+
puts "planter v#{Planter::VERSION}"
|
90
|
+
Process.exit 0
|
91
|
+
elsif options[:help]
|
92
|
+
if ARGV.count.zero?
|
93
|
+
puts opts
|
94
|
+
else
|
95
|
+
list_vars(ARGV[0])
|
96
|
+
end
|
97
|
+
Process.exit 0
|
98
|
+
elsif ARGV.count.zero?
|
99
|
+
raise Planter::Errors::ArgumentError.new 'Template argument required'
|
100
|
+
end
|
101
|
+
|
102
|
+
ARGV.each do |template|
|
103
|
+
Planter.config = template
|
104
|
+
app = Planter::Plant.new
|
105
|
+
app.plant
|
106
|
+
end
|
data/debug.log
ADDED
File without changes
|
data/docker/Dockerfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
FROM ruby:3.0.1
|
2
|
+
# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
3
|
+
RUN mkdir /planter
|
4
|
+
WORKDIR /planter
|
5
|
+
|
6
|
+
RUN gem install bundler:2.2.29
|
7
|
+
RUN apt-get update -y
|
8
|
+
RUN apt-get install -y less vim
|
9
|
+
COPY ./docker/inputrc /root/.inputrc
|
10
|
+
COPY ./docker/bash_profile /root/.bash_profile
|
11
|
+
RUN mkdir -p /root/.config/planter/templates/test
|
12
|
+
CMD ["/planter/scripts/runtests.sh"]
|
@@ -0,0 +1,12 @@
|
|
1
|
+
FROM ruby:2.6
|
2
|
+
# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
3
|
+
RUN mkdir /howzit
|
4
|
+
WORKDIR /howzit
|
5
|
+
# COPY ./ /howzit/
|
6
|
+
RUN gem install bundler:2.2.29
|
7
|
+
RUN apt-get update -y
|
8
|
+
RUN apt-get install -y less vim
|
9
|
+
COPY ./docker/inputrc /root/.inputrc
|
10
|
+
COPY ./docker/bash_profile /root/.bash_profile
|
11
|
+
RUN mkdir -p /root/.config/planter/templates/test
|
12
|
+
CMD ["/planter/scripts/runtests.sh"]
|
@@ -0,0 +1,12 @@
|
|
1
|
+
FROM ruby:2.7
|
2
|
+
# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
3
|
+
RUN mkdir /howzit
|
4
|
+
WORKDIR /howzit
|
5
|
+
# COPY ./ /howzit/
|
6
|
+
RUN gem install bundler:2.2.29
|
7
|
+
RUN apt-get update -y
|
8
|
+
RUN apt-get install -y less vim
|
9
|
+
COPY ./docker/inputrc /root/.inputrc
|
10
|
+
COPY ./docker/bash_profile /root/.bash_profile
|
11
|
+
RUN mkdir -p /root/.config/planter/templates/test
|
12
|
+
CMD ["/planter/scripts/runtests.sh"]
|
@@ -0,0 +1,11 @@
|
|
1
|
+
FROM ruby:3.0.0
|
2
|
+
# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
3
|
+
RUN mkdir /howzit
|
4
|
+
WORKDIR /howzit
|
5
|
+
# COPY ./ /howzit/
|
6
|
+
RUN gem install bundler:2.2.29
|
7
|
+
RUN apt-get update -y
|
8
|
+
RUN apt-get install -y less vim
|
9
|
+
COPY ./docker/inputrc /root/.inputrc
|
10
|
+
COPY ./docker/bash_profile /root/.bash_profile
|
11
|
+
CMD ["/planter/scripts/runtests.sh"]
|
data/docker/bash_profile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
export GLI_DEBUG=true
|
3
|
+
export EDITOR="/usr/bin/vim"
|
4
|
+
alias b="bundle exec bin/plant"
|
5
|
+
alias quit="exit"
|
6
|
+
|
7
|
+
shopt -s nocaseglob
|
8
|
+
shopt -s histappend
|
9
|
+
shopt -s histreedit
|
10
|
+
shopt -s histverify
|
11
|
+
shopt -s cmdhist
|
12
|
+
|
13
|
+
cd /planter
|
14
|
+
bundle install
|
15
|
+
gem install pkg/*.gem
|
data/docker/inputrc
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
"\e[3~": delete-char
|
2
|
+
"\ex": 'cd !$ \015ls\015'
|
3
|
+
"\ez": 'cd -\015'
|
4
|
+
"\e\C-m": '\C-a "$(\C-e|fzf)"\C-a'
|
5
|
+
"\e/": '"$(!!|fzf)"\C-a \C-m\C-m'
|
6
|
+
# these allow you to use alt+left/right arrow keys
|
7
|
+
# to jump the cursor over words
|
8
|
+
"\e[1;5C": forward-word
|
9
|
+
"\e[1;5D": backward-word
|
10
|
+
# "\e[D": backward-word
|
11
|
+
# "\e[C": forward-word
|
12
|
+
"\ea": menu-complete
|
13
|
+
# TAB: menu-complete
|
14
|
+
# "\e[Z": "\e-1\C-i"
|
15
|
+
|
16
|
+
"\e\C-l": history-and-alias-expand-line
|
17
|
+
|
18
|
+
# these allow you to start typing a command and
|
19
|
+
# use the up/down arrow to auto complete from
|
20
|
+
# commands in your history
|
21
|
+
"\e[B": history-search-forward
|
22
|
+
"\e[A": history-search-backward
|
23
|
+
"\ew": history-search-backward
|
24
|
+
"\es": history-search-forward
|
25
|
+
# this lets you hit tab to auto-complete a file or
|
26
|
+
# directory name ignoring case
|
27
|
+
set completion-ignore-case On
|
28
|
+
set mark-symlinked-directories On
|
29
|
+
set completion-prefix-display-length 2
|
30
|
+
set bell-style none
|
31
|
+
# set bell-style visible
|
32
|
+
set meta-flag on
|
33
|
+
set convert-meta off
|
34
|
+
set input-meta on
|
35
|
+
set output-meta on
|
36
|
+
set show-all-if-ambiguous on
|
37
|
+
set show-all-if-unmodified on
|
38
|
+
set completion-map-case on
|
39
|
+
set visible-stats on
|
40
|
+
|
41
|
+
# Do history expansion when space entered?
|
42
|
+
$if bash
|
43
|
+
Space: magic-space
|
44
|
+
$endif
|
45
|
+
|
46
|
+
# Show extra file information when completing, like `ls -F` does
|
47
|
+
set visible-stats on
|
48
|
+
|
49
|
+
# Be more intelligent when autocompleting by also looking at the text after
|
50
|
+
# the cursor. For example, when the current line is "cd ~/src/mozil", and
|
51
|
+
# the cursor is on the "z", pressing Tab will not autocomplete it to "cd
|
52
|
+
# ~/src/mozillail", but to "cd ~/src/mozilla". (This is supported by the
|
53
|
+
# Readline used by Bash 4.)
|
54
|
+
set skip-completed-text on
|
55
|
+
|
56
|
+
# Use Alt/Meta + Delete to delete the preceding word
|
57
|
+
"\e[3;3~": kill-word
|
data/lib/.rubocop.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
inherit_from: ../.rubocop.yml
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Planter
|
4
|
+
# Array helpers
|
5
|
+
class ::Array
|
6
|
+
##
|
7
|
+
## Convert an array of "(c)hoices" to abbrevation. If a default character is
|
8
|
+
## provided it will be highlighted. Output is a color template, unprocessed.
|
9
|
+
##
|
10
|
+
## @example ["(c)hoice", "(o)ther"].abbr_choices #=> "[c/o]"
|
11
|
+
##
|
12
|
+
## @param default [String] The color templated output string
|
13
|
+
##
|
14
|
+
def abbr_choices(default: nil)
|
15
|
+
chars = join(' ').scan(/\((.)\)/).map { |c| c[0] }
|
16
|
+
out = String.new
|
17
|
+
out << '{xdw}['
|
18
|
+
out << chars.map do |c|
|
19
|
+
if default && c.downcase == default.downcase
|
20
|
+
"{xbc}#{c}"
|
21
|
+
else
|
22
|
+
"{xbw}#{c}"
|
23
|
+
end
|
24
|
+
end.join('{dw}/')
|
25
|
+
out << '{dw}]{x}'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,370 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Planter
|
4
|
+
# Cribbed from <https://github.com/flori/term-ansicolor>
|
5
|
+
# Terminal output color functions.
|
6
|
+
module Color
|
7
|
+
# Regexp to match excape sequences
|
8
|
+
ESCAPE_REGEX = /(?<=\[)(?:(?:(?:[349]|10)[0-9]|[0-9])?;?)+(?=m)/
|
9
|
+
|
10
|
+
# All available color names. Available as methods and string extensions.
|
11
|
+
#
|
12
|
+
# @example Use a color as a method. Color reset will be added to end of string.
|
13
|
+
# Color.yellow('This text is yellow') => "\e[33mThis text is yellow\e[0m"
|
14
|
+
#
|
15
|
+
# @example Use a color as a string extension. Color reset added automatically.
|
16
|
+
# 'This text is green'.green => "\e[1;32mThis text is green\e[0m"
|
17
|
+
#
|
18
|
+
# @example Send a text string as a color
|
19
|
+
# Color.send('red') => "\e[31m"
|
20
|
+
ATTRIBUTES = [
|
21
|
+
[:clear, 0], # String#clear is already used to empty string in Ruby 1.9
|
22
|
+
[:reset, 0], # synonym for :clear
|
23
|
+
[:bold, 1],
|
24
|
+
[:dark, 2],
|
25
|
+
[:italic, 3], # not widely implemented
|
26
|
+
[:underline, 4],
|
27
|
+
[:underscore, 4], # synonym for :underline
|
28
|
+
[:blink, 5],
|
29
|
+
[:rapid_blink, 6], # not widely implemented
|
30
|
+
[:negative, 7], # no reverse because of String#reverse
|
31
|
+
[:concealed, 8],
|
32
|
+
[:strikethrough, 9], # not widely implemented
|
33
|
+
[:strike, 9], # not widely implemented
|
34
|
+
[:black, 30],
|
35
|
+
[:red, 31],
|
36
|
+
[:green, 32],
|
37
|
+
[:yellow, 33],
|
38
|
+
[:blue, 34],
|
39
|
+
[:magenta, 35],
|
40
|
+
[:purple, 35],
|
41
|
+
[:cyan, 36],
|
42
|
+
[:white, 37],
|
43
|
+
[:bgblack, 40],
|
44
|
+
[:bgred, 41],
|
45
|
+
[:bggreen, 42],
|
46
|
+
[:bgyellow, 43],
|
47
|
+
[:bgblue, 44],
|
48
|
+
[:bgmagenta, 45],
|
49
|
+
[:bgpurple, 45],
|
50
|
+
[:bgcyan, 46],
|
51
|
+
[:bgwhite, 47],
|
52
|
+
[:boldblack, 90],
|
53
|
+
[:boldred, 91],
|
54
|
+
[:boldgreen, 92],
|
55
|
+
[:boldyellow, 93],
|
56
|
+
[:boldblue, 94],
|
57
|
+
[:boldmagenta, 95],
|
58
|
+
[:boldpurple, 95],
|
59
|
+
[:boldcyan, 96],
|
60
|
+
[:boldwhite, 97],
|
61
|
+
[:boldbgblack, 100],
|
62
|
+
[:boldbgred, 101],
|
63
|
+
[:boldbggreen, 102],
|
64
|
+
[:boldbgyellow, 103],
|
65
|
+
[:boldbgblue, 104],
|
66
|
+
[:boldbgmagenta, 105],
|
67
|
+
[:boldbgpurple, 105],
|
68
|
+
[:boldbgcyan, 106],
|
69
|
+
[:boldbgwhite, 107],
|
70
|
+
[:softpurple, '0;35;40'],
|
71
|
+
[:hotpants, '7;34;40'],
|
72
|
+
[:knightrider, '7;30;40'],
|
73
|
+
[:flamingo, '7;31;47'],
|
74
|
+
[:yeller, '1;37;43'],
|
75
|
+
[:whiteboard, '1;30;47'],
|
76
|
+
[:chalkboard, '1;37;40'],
|
77
|
+
[:led, '0;32;40'],
|
78
|
+
[:redacted, '0;30;40'],
|
79
|
+
[:alert, '1;31;43'],
|
80
|
+
[:error, '1;37;41'],
|
81
|
+
[:default, '0;39']
|
82
|
+
].map(&:freeze).freeze
|
83
|
+
|
84
|
+
# Array of attribute keys only
|
85
|
+
ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
|
86
|
+
|
87
|
+
# Returns true if Color supports the +feature+.
|
88
|
+
#
|
89
|
+
# The feature :clear, that is mixing the clear color attribute into String,
|
90
|
+
# is only supported on ruby implementations, that do *not* already
|
91
|
+
# implement the String#clear method. It's better to use the reset color
|
92
|
+
# attribute instead.
|
93
|
+
def support?(feature)
|
94
|
+
case feature
|
95
|
+
when :clear
|
96
|
+
!String.instance_methods(false).map(&:to_sym).include?(:clear)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Template coloring
|
101
|
+
class ::String
|
102
|
+
##
|
103
|
+
## Shortcut for #template
|
104
|
+
##
|
105
|
+
## @return [String] colorized string
|
106
|
+
##
|
107
|
+
def x
|
108
|
+
Color.template(self)
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
## Extract the longest valid %color name from a string.
|
113
|
+
##
|
114
|
+
## Allows %colors to bleed into other text and still
|
115
|
+
## be recognized, e.g. %greensomething still finds
|
116
|
+
## %green.
|
117
|
+
##
|
118
|
+
## @return [String] a valid color name
|
119
|
+
##
|
120
|
+
def validate_color
|
121
|
+
valid_color = nil
|
122
|
+
compiled = ''
|
123
|
+
normalize_color.chars.each do |char|
|
124
|
+
compiled += char
|
125
|
+
if Color.attributes.include?(compiled.to_sym) || compiled =~ /^([fb]g?)?#([a-f0-9]{6})$/i
|
126
|
+
valid_color = compiled
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
valid_color
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
## Normalize a color name, removing underscores,
|
135
|
+
## replacing "bright" with "bold", and converting
|
136
|
+
## bgbold to boldbg
|
137
|
+
##
|
138
|
+
## @return [String] Normalized color name
|
139
|
+
##
|
140
|
+
def normalize_color
|
141
|
+
delete('_').sub(/bright/i, 'bold').sub(/bgbold/, 'boldbg')
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get the calculated ANSI color at the end of the
|
145
|
+
# string
|
146
|
+
#
|
147
|
+
# @return ANSI escape sequence to match color
|
148
|
+
#
|
149
|
+
def last_color_code
|
150
|
+
m = scan(ESCAPE_REGEX)
|
151
|
+
|
152
|
+
em = ['0']
|
153
|
+
fg = nil
|
154
|
+
bg = nil
|
155
|
+
rgbf = nil
|
156
|
+
rgbb = nil
|
157
|
+
|
158
|
+
m.each do |c|
|
159
|
+
case c
|
160
|
+
when '0'
|
161
|
+
em = ['0']
|
162
|
+
fg, bg, rgbf, rgbb = nil
|
163
|
+
when /^[34]8/
|
164
|
+
case c
|
165
|
+
when /^3/
|
166
|
+
fg = nil
|
167
|
+
rgbf = c
|
168
|
+
when /^4/
|
169
|
+
bg = nil
|
170
|
+
rgbb = c
|
171
|
+
end
|
172
|
+
else
|
173
|
+
c.split(';').each do |i|
|
174
|
+
x = i.to_i
|
175
|
+
if x <= 9
|
176
|
+
em << x
|
177
|
+
elsif x >= 30 && x <= 39
|
178
|
+
rgbf = nil
|
179
|
+
fg = x
|
180
|
+
elsif x >= 40 && x <= 49
|
181
|
+
rgbb = nil
|
182
|
+
bg = x
|
183
|
+
elsif x >= 90 && x <= 97
|
184
|
+
rgbf = nil
|
185
|
+
fg = x
|
186
|
+
elsif x >= 100 && x <= 107
|
187
|
+
rgbb = nil
|
188
|
+
bg = x
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
escape = "\e[#{em.join(';')}m"
|
195
|
+
escape += "\e[#{rgbb}m" if rgbb
|
196
|
+
escape += "\e[#{rgbf}m" if rgbf
|
197
|
+
escape + "\e[#{[fg, bg].delete_if(&:nil?).join(';')}m"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class << self
|
202
|
+
# Returns true if the coloring function of this module
|
203
|
+
# is switched on, false otherwise.
|
204
|
+
def coloring?
|
205
|
+
@coloring
|
206
|
+
end
|
207
|
+
|
208
|
+
attr_writer :coloring
|
209
|
+
|
210
|
+
##
|
211
|
+
## Enables colored output
|
212
|
+
##
|
213
|
+
## @example Turn color on or off based on TTY
|
214
|
+
## Color.coloring = STDOUT.isatty
|
215
|
+
def coloring
|
216
|
+
@coloring ||= true
|
217
|
+
end
|
218
|
+
|
219
|
+
##
|
220
|
+
## Convert a template string to a colored string.
|
221
|
+
## Colors are specified with single letters inside
|
222
|
+
## curly braces. Uppercase changes background color.
|
223
|
+
##
|
224
|
+
## w: white, k: black, g: green, l: blue, y: yellow, c: cyan,
|
225
|
+
## m: magenta, r: red, b: bold, u: underline, i: italic,
|
226
|
+
## x: reset (remove background, color, emphasis)
|
227
|
+
##
|
228
|
+
## Also accepts {(#)RGB} and {(#)RRGGBB} strings. Put a b before
|
229
|
+
## the hash to make it a background color
|
230
|
+
##
|
231
|
+
## @example Convert a templated string
|
232
|
+
## Color.template('{Rwb}Warning:{x} {w}you look a little {g}ill{x}')
|
233
|
+
##
|
234
|
+
## @example Convert using RGB colors
|
235
|
+
## Color.template('{#f0a}This is an RGB color')
|
236
|
+
##
|
237
|
+
## @param input [String, Array] The template
|
238
|
+
## string. If this is an array, the
|
239
|
+
## elements will be joined with a
|
240
|
+
## space.
|
241
|
+
##
|
242
|
+
## @return [String] Colorized string
|
243
|
+
##
|
244
|
+
def template(input)
|
245
|
+
input = input.join(' ') if input.is_a? Array
|
246
|
+
return input.gsub(/(?<!\\)\{(\w+)\}/i, '') unless Color.coloring?
|
247
|
+
|
248
|
+
input = input.gsub(/(?<!\\)\{((?:[fb]g?)?#[a-f0-9]{3,6})\}/i) do
|
249
|
+
hex = Regexp.last_match(1)
|
250
|
+
rgb(hex)
|
251
|
+
end
|
252
|
+
|
253
|
+
fmt = input.gsub(/%/, '%%')
|
254
|
+
fmt = fmt.gsub(/(?<!\\)\{(\w+)\}/i) do
|
255
|
+
Regexp.last_match(1).chars.map { |c| "%<#{c}>s" }.join('')
|
256
|
+
end
|
257
|
+
|
258
|
+
colors = { w: white, k: black, g: green, l: blue,
|
259
|
+
y: yellow, c: cyan, m: magenta, r: red,
|
260
|
+
W: bgwhite, K: bgblack, G: bggreen, L: bgblue,
|
261
|
+
Y: bgyellow, C: bgcyan, M: bgmagenta, R: bgred,
|
262
|
+
d: dark, b: bold, u: underline, i: italic, x: reset }
|
263
|
+
|
264
|
+
format(fmt, colors)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
ATTRIBUTES.each do |c, v|
|
269
|
+
new_method = <<-EOSCRIPT
|
270
|
+
# Color string as #{c}
|
271
|
+
def #{c}(string = nil)
|
272
|
+
result = ''
|
273
|
+
result << "\e[#{v}m" if Color.coloring?
|
274
|
+
if block_given?
|
275
|
+
result << yield
|
276
|
+
elsif string.respond_to?(:to_str)
|
277
|
+
result << string.to_str
|
278
|
+
elsif respond_to?(:to_str)
|
279
|
+
result << to_str
|
280
|
+
else
|
281
|
+
return result #only switch on
|
282
|
+
end
|
283
|
+
result << "\e[0m" if Color.coloring?
|
284
|
+
result
|
285
|
+
end
|
286
|
+
EOSCRIPT
|
287
|
+
|
288
|
+
module_eval(new_method)
|
289
|
+
|
290
|
+
next unless /bold/.match?(c)
|
291
|
+
|
292
|
+
# Accept brightwhite in addition to boldwhite
|
293
|
+
new_method = <<-EOSCRIPT
|
294
|
+
# color string as #{c}
|
295
|
+
def #{c.to_s.sub(/bold/, 'bright')}(string = nil)
|
296
|
+
result = ''
|
297
|
+
result << "\e[#{v}m" if Color.coloring?
|
298
|
+
if block_given?
|
299
|
+
result << yield
|
300
|
+
elsif string.respond_to?(:to_str)
|
301
|
+
result << string.to_str
|
302
|
+
elsif respond_to?(:to_str)
|
303
|
+
result << to_str
|
304
|
+
else
|
305
|
+
return result #only switch on
|
306
|
+
end
|
307
|
+
result << "\e[0m" if Color.coloring?
|
308
|
+
result
|
309
|
+
end
|
310
|
+
EOSCRIPT
|
311
|
+
|
312
|
+
module_eval(new_method)
|
313
|
+
end
|
314
|
+
|
315
|
+
##
|
316
|
+
## Generate escape codes for hex colors
|
317
|
+
##
|
318
|
+
## @param hex [String] The hexadecimal color code
|
319
|
+
##
|
320
|
+
## @return [String] ANSI escape string
|
321
|
+
##
|
322
|
+
def rgb(hex)
|
323
|
+
is_bg = /^bg?#/.match?(hex)
|
324
|
+
hex_string = hex.sub(/^([fb]g?)?#/, '')
|
325
|
+
|
326
|
+
if hex_string.length == 3
|
327
|
+
parts = hex_string.match(/(?<r>.)(?<g>.)(?<b>.)/)
|
328
|
+
|
329
|
+
t = []
|
330
|
+
%w[r g b].each do |e|
|
331
|
+
t << parts[e]
|
332
|
+
t << parts[e]
|
333
|
+
end
|
334
|
+
hex_string = t.join('')
|
335
|
+
end
|
336
|
+
|
337
|
+
parts = hex_string.match(/(?<r>..)(?<g>..)(?<b>..)/)
|
338
|
+
t = []
|
339
|
+
%w[r g b].each do |e|
|
340
|
+
t << parts[e].hex
|
341
|
+
end
|
342
|
+
|
343
|
+
"\e[#{is_bg ? '48' : '38'};2;#{t.join(';')}m"
|
344
|
+
end
|
345
|
+
|
346
|
+
# Regular expression that is used to scan for ANSI-sequences while
|
347
|
+
# uncoloring strings.
|
348
|
+
COLORED_REGEXP = /\e\[(?:(?:(?:[349]|10)[0-9]|[0-9])?;?)+m/
|
349
|
+
|
350
|
+
# Returns an uncolored version of the string, that is all
|
351
|
+
# ANSI-sequences are stripped from the string.
|
352
|
+
def uncolor(string = nil) # :yields:
|
353
|
+
if block_given?
|
354
|
+
yield.to_str.gsub(COLORED_REGEXP, '')
|
355
|
+
elsif string.respond_to?(:to_str)
|
356
|
+
string.to_str.gsub(COLORED_REGEXP, '')
|
357
|
+
elsif respond_to?(:to_str)
|
358
|
+
to_str.gsub(COLORED_REGEXP, '')
|
359
|
+
else
|
360
|
+
''
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# Returns an array of all Color attributes as symbols.
|
365
|
+
def attributes
|
366
|
+
ATTRIBUTE_NAMES
|
367
|
+
end
|
368
|
+
extend self
|
369
|
+
end
|
370
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Planter
|
4
|
+
## Error handlers
|
5
|
+
module Errors
|
6
|
+
## Exit codes
|
7
|
+
EXIT_CODES = {
|
8
|
+
argument: 12,
|
9
|
+
canceled: 1,
|
10
|
+
config: 127,
|
11
|
+
git: 129
|
12
|
+
}.deep_freeze
|
13
|
+
|
14
|
+
## Argument error class
|
15
|
+
class InputError < StandardError
|
16
|
+
def initialize(msg = nil)
|
17
|
+
msg = msg ? "Input: #{msg}" : 'Canceled'
|
18
|
+
|
19
|
+
Planter.notify(msg, :error, exit_code: EXIT_CODES[:canceled])
|
20
|
+
|
21
|
+
super(msg)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
## Argument error class
|
26
|
+
class ArgumentError < StandardError
|
27
|
+
def initialize(msg = nil)
|
28
|
+
msg = msg ? "Argument error: #{msg}" : 'Argument error'
|
29
|
+
|
30
|
+
Planter.notify(msg, :error, exit_code: EXIT_CODES[:argument])
|
31
|
+
|
32
|
+
super(msg)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
## Git error class
|
37
|
+
class GitError < StandardError
|
38
|
+
def initialize(msg = nil)
|
39
|
+
msg = msg ? "Git: #{msg}" : 'Git error'
|
40
|
+
|
41
|
+
Planter.spinner.error('(Error)')
|
42
|
+
Planter.notify(msg, :error, exit_code: EXIT_CODES[:git])
|
43
|
+
|
44
|
+
super(msg)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Configuration error class
|
49
|
+
class ConfigError < StandardError
|
50
|
+
def initialize(msg = nil)
|
51
|
+
msg = msg ? "Config: #{msg}" : 'Configuration error'
|
52
|
+
Planter.spinner.error('(Error)')
|
53
|
+
Planter.notify(msg, :error, exit_code: EXIT_CODES[:config])
|
54
|
+
|
55
|
+
super(msg)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|