hansi 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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