hansi 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/.rspec +4 -0
- data/.travis.yml +2 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +230 -0
- data/hansi.gemspec +22 -0
- data/hansi.png +0 -0
- data/lib/hansi/ansi_code.rb +16 -0
- data/lib/hansi/color.rb +115 -0
- data/lib/hansi/color_parser.rb +93 -0
- data/lib/hansi/color_renderer.rb +20 -0
- data/lib/hansi/mode_detector.rb +201 -0
- data/lib/hansi/palettes.rb +591 -0
- data/lib/hansi/sexp_renderer.rb +27 -0
- data/lib/hansi/special.rb +25 -0
- data/lib/hansi/string_renderer.rb +103 -0
- data/lib/hansi/theme.rb +66 -0
- data/lib/hansi/themes.rb +24 -0
- data/lib/hansi/version.rb +3 -0
- data/lib/hansi.rb +53 -0
- data/spec/hansi/color_parser_spec.rb +17 -0
- data/spec/hansi/color_renderer_spec.rb +6 -0
- data/spec/hansi/color_spec.rb +46 -0
- data/spec/hansi/mode_detector_spec.rb +35 -0
- data/spec/hansi/sexp_renderer_spec.rb +7 -0
- data/spec/hansi/special_spec.rb +6 -0
- data/spec/hansi/string_renderer_spec.rb +32 -0
- data/spec/hansi/theme_spec.rb +43 -0
- data/spec/hansi_spec.rb +41 -0
- data/spec/support/coverage.rb +14 -0
- data/spec/support/env.rb +10 -0
- data/spec/support/escape_matcher.rb +28 -0
- data/spec/support/parse_matcher.rb +28 -0
- data/spec/support/render_matcher.rb +28 -0
- data/spec/support.rb +6 -0
- metadata +59 -10
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Hansi
|
4
|
+
class ModeDetector
|
5
|
+
TERMS = { # only includes $TERM values that don't have the color amout in ther name and support more than 8 colors
|
6
|
+
0 => ["dummy"],
|
7
|
+
15 => ["emu"],
|
8
|
+
16 => [
|
9
|
+
"amiga-vnc", "d430-dg", "d430-unix", "d430-unix-25", "d430-unix-s", "d430-unix-sr", "d430-unix-w", "d430c-dg", "d430c-unix", "d430c-unix-25",
|
10
|
+
"d430c-unix-s", "d430c-unix-sr", "d430c-unix-w", "d470", "d470-7b", "d470-dg", "d470c", "d470c-7b", "d470c-dg", "dg+color", "dg+fixed", "dgmode+color",
|
11
|
+
"dgunix+fixed", "hp+color", "hp2397", "hp2397a", "ncr260wy325pp", "ncr260wy325wpp", "ncr260wy350pp", "ncr260wy350wpp", "nsterm+c", "nsterm-7-c",
|
12
|
+
"nsterm-7-c-s", "nsterm-acs-c", "nsterm-acs-c-s", "nsterm-c", "nsterm-c-7", "nsterm-c-acs", "nsterm-c-s", "nsterm-c-s-7", "nsterm-c-s-acs"
|
13
|
+
],
|
14
|
+
52 => [
|
15
|
+
"d430-dg-ccc", "d430-unix-25-ccc", "d430-unix-ccc", "d430-unix-s-ccc", "d430-unix-sr-ccc", "d430-unix-w-ccc", "d430c-dg-ccc", "d430c-unix-25-ccc",
|
16
|
+
"d430c-unix-ccc", "d430c-unix-s-ccc", "d430c-unix-sr-ccc", "d430c-unix-w-ccc", "dg+ccc", "dgunix+ccc"
|
17
|
+
],
|
18
|
+
64 => [
|
19
|
+
"hpterm-color", "wy370", "wy370-101k", "wy370-105k", "wy370-EPC", "wy370-nk", "wy370-rv", "wy370-vb", "wy370-w", "wy370-wvb", "wyse370"
|
20
|
+
],
|
21
|
+
256 => ["Eterm"]
|
22
|
+
}
|
23
|
+
|
24
|
+
TRUE_COLOR_COMMANDS = [ "finalterm", "konsole", "sakura", "roxterm", "yakuake", "st", "tilda" ]
|
25
|
+
COMMANDS = [
|
26
|
+
"gnome-terminal", "gnome-terminal-server", "xterm", "iTerm2", "guake", "termit", "evilvte", "terminator",
|
27
|
+
"lxterminal", "terminology", "xfce4-terminal", "stjerm", *TRUE_COLOR_COMMANDS
|
28
|
+
]
|
29
|
+
|
30
|
+
attr_reader :env, :supported, :io
|
31
|
+
def initialize(env, shell_out: nil, io: nil, supported: [0, 8, 16, 88, 256, Hansi::TRUE_COLOR])
|
32
|
+
shell_out = true if shell_out.nil? and env == ENV
|
33
|
+
io = $stdout if io.nil? and env == ENV
|
34
|
+
@env, @shell_out, @supported , @io = env, shell_out, supported, io
|
35
|
+
end
|
36
|
+
|
37
|
+
def mode
|
38
|
+
@mode ||= begin
|
39
|
+
mode = from_tput || from_term_program || from_term
|
40
|
+
mode = Hansi::TRUE_COLOR if mode == 256 and maximum >= Hansi::TRUE_COLOR and true_color?
|
41
|
+
mode ||= 8 if io and io.tty?
|
42
|
+
mode ||= 0
|
43
|
+
supported?(mode) ? mode : minimum
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def from_tput
|
48
|
+
return unless shell_out?
|
49
|
+
colors = `tput #{"-T#{term}" if term} colors`.to_i
|
50
|
+
colors if colors > 8
|
51
|
+
rescue Errno::ENOENT
|
52
|
+
end
|
53
|
+
|
54
|
+
def from_term_program
|
55
|
+
term_program = env['TERM_PROGRAM']
|
56
|
+
256 if term_program == 'Apple_Terminal' or term_program == 'MacTerm'
|
57
|
+
end
|
58
|
+
|
59
|
+
def from_term
|
60
|
+
case term
|
61
|
+
when /[\-\+](\d+)color/ then $1.to_i
|
62
|
+
when /[\-\+](\d+)bit/ then 2 ** $1.to_i
|
63
|
+
when /[\-\+](\d+)byte/ then 2 ** (8*$1.to_i)
|
64
|
+
when 'xterm' then from_xterm
|
65
|
+
else TERMS.keys.detect { |key| TERMS[key].include? term }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def from_xterm
|
70
|
+
terminal_command ? 256 : 16
|
71
|
+
end
|
72
|
+
|
73
|
+
def term
|
74
|
+
env['TERM']
|
75
|
+
end
|
76
|
+
|
77
|
+
def true_color?
|
78
|
+
return true if TRUE_COLOR_COMMANDS.include? terminal_command
|
79
|
+
return iterm_true_color? if iterm?
|
80
|
+
return xterm_true_color? if actual_xterm?
|
81
|
+
return gnome_true_color? if gnome_terminal?
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
def gnome_terminal?
|
86
|
+
terminal_command == "gnome-terminal" or terminal_command == "gnome-terminal-server"
|
87
|
+
end
|
88
|
+
|
89
|
+
def actual_xterm?
|
90
|
+
terminal_command == "xterm"
|
91
|
+
end
|
92
|
+
|
93
|
+
def iterm?
|
94
|
+
env.include? 'ITERM_SESSION_ID' or env['TERM_PROGRAM'] == 'iTerm.app' or terminal_command == 'iTerm2'
|
95
|
+
end
|
96
|
+
|
97
|
+
def xterm_true_color?
|
98
|
+
`xterm -version`[/\d+/].to_i >= 282
|
99
|
+
rescue Errno::ENOENT
|
100
|
+
false
|
101
|
+
end
|
102
|
+
|
103
|
+
def gnome_true_color?
|
104
|
+
major, minor = `gnome-terminal --version`[/\d+\.\d+/].split(?.).map(&:to_i)
|
105
|
+
major == 3 ? minor > 11 : major > 3
|
106
|
+
rescue Errno::ENOENT
|
107
|
+
false
|
108
|
+
end
|
109
|
+
|
110
|
+
def iterm_true_color?
|
111
|
+
return false unless iterm_version
|
112
|
+
major, minor, patch = iterm_version.split('.').map(&:to_i)
|
113
|
+
if major == 2 and minor == 9
|
114
|
+
patch > 20130908
|
115
|
+
else
|
116
|
+
major >= 3
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def iterm_version
|
121
|
+
@iterm_version ||= env['TERM_PROGRAM_VERSION']
|
122
|
+
@iterm_version ||= if shell_out? and iterm_path = commands.detect { |c| c["Contents/MacOS/iTerm"] }
|
123
|
+
iterm_path = iterm_path[/^(.*) [^ ]+$/, 1] while iterm_path and not File.exist?(iterm_path)
|
124
|
+
info_path = File.expand_path('../../Info.plist', iterm_path)
|
125
|
+
`defaults read #{Shellwords.escape(info_path)} CFBundleVersion`.chomp if File.exist?(info_path) and system 'which defaults >/dev/null'
|
126
|
+
end
|
127
|
+
rescue Errno::ENOENT
|
128
|
+
end
|
129
|
+
|
130
|
+
def terminal_command(command = full_terminal_command)
|
131
|
+
command &&= File.basename(command)
|
132
|
+
command &&= command[/\S+/]
|
133
|
+
command
|
134
|
+
end
|
135
|
+
|
136
|
+
def full_terminal_command
|
137
|
+
commands.detect do |command|
|
138
|
+
command = terminal_command(command)
|
139
|
+
COMMANDS.include?(command) or command == env['TERM_PROGRAM']
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def commands
|
144
|
+
@commands ||= begin
|
145
|
+
commands = []
|
146
|
+
pid = Process.pid
|
147
|
+
processes = self.processes
|
148
|
+
while pid and processes.include? pid
|
149
|
+
commands << processes[pid][:command]
|
150
|
+
pid = processes[pid][:ppid]
|
151
|
+
end
|
152
|
+
commands
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def processes
|
157
|
+
return {} unless shell_out?
|
158
|
+
@processes ||= begin
|
159
|
+
ps = `ps -A -o pid,ppid,command`.scan(/^\s*(\d+)\s+(\d+)\s+(.+)$/)
|
160
|
+
Hash[ps.map { |pid, ppid, command| [pid.to_i, ppid: ppid.to_i, command: command] }]
|
161
|
+
end
|
162
|
+
rescue Errno::ENOENT
|
163
|
+
{}
|
164
|
+
end
|
165
|
+
|
166
|
+
def shell_out?
|
167
|
+
@shell_out and not windows?
|
168
|
+
end
|
169
|
+
|
170
|
+
def windows?
|
171
|
+
File::ALT_SEPARATOR == "\\"
|
172
|
+
end
|
173
|
+
|
174
|
+
def supported?(value)
|
175
|
+
case supported
|
176
|
+
when Array then supported.include? value
|
177
|
+
when true, false then supported
|
178
|
+
when Integer then supported == value
|
179
|
+
else false
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def maximum
|
184
|
+
case supported
|
185
|
+
when Array then supported.max
|
186
|
+
when true then 1/0.0
|
187
|
+
when Integer then supported
|
188
|
+
else 0
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def minimum
|
193
|
+
case supported
|
194
|
+
when Array then supported.min
|
195
|
+
when true then 8
|
196
|
+
when Integer then supported
|
197
|
+
else 0
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|