termdump 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ require 'shellwords'
2
+
3
+ module TermDump
4
+ module TerminalHelper
5
+
6
+ # Doing convertion below and join them with '+'
7
+ # case 0 : <Ctrl><Shift><Alt><Super> => Ctrl Shift Alt Super
8
+ # case 1 : [A-Z] => [a-z]
9
+ # case 2 : <Primary> => Ctrl
10
+ # case 3 : [0-9] [a-z] UpDownLeftRight F[0-12] Return space... => not changed
11
+ # For example,
12
+ # '<Primary><Shift>A' => 'Ctrl+Shift+a'
13
+ # '<Alt>space' => 'Alt+space'
14
+ def convert_key_sequence in_sequence
15
+ in_sequence.tr_s!('[<>]', ' ')
16
+ out_sequence = in_sequence.split.map do |key|
17
+ if /^[[:upper:]]$/.match(key)
18
+ key.downcase
19
+ elsif key == 'Primary'
20
+ 'Ctrl'
21
+ else
22
+ key
23
+ end
24
+ end
25
+ out_sequence.join('+')
26
+ end
27
+
28
+ # escape the command string so that it can be executed safily in the shell
29
+ def escape cmd
30
+ # not woking under windows, but I don't think I will support windows :)
31
+ Shellwords.escape cmd
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,44 @@
1
+ require_relative 'base/base'
2
+
3
+ module TermDump
4
+ # This Terminal class is for [gnome-terminal](https://wiki.gnome.org/Apps/Terminal)
5
+ class Terminal < BasicTerminal
6
+ def initialize config
7
+ @user_defined_config = config
8
+ @keybindings = '/apps/gnome-terminal/keybindings'
9
+ @config = {
10
+ 'new_window' => get_configure_key('new_window'),
11
+ 'new_tab' => get_configure_key('new_tab')
12
+ }
13
+
14
+ @default_config = {
15
+ 'new_window' => 'ctrl+alt+t',
16
+ 'new_tab' => 'ctrl+shift+t',
17
+ }
18
+ end
19
+
20
+ def get_configure_key key
21
+ value = IO.popen(
22
+ "gconftool -g '#{@keybindings}/#{key}' 2>/dev/null").read.chomp
23
+ convert_key_sequence(value) if value != ''
24
+ end
25
+
26
+ def exec cwd, cmd
27
+ sleep 0.5
28
+ `xdotool getactivewindow type #{escape("cd #{cwd}\n")}`
29
+ `xdotool getactivewindow type #{escape("#{cmd}\n")}` unless cmd.nil?
30
+ end
31
+
32
+ def window name, cwd, cmd
33
+ # `getactivewindow key` not work on gnome-terminal, bug?
34
+ `xdotool key #{configure 'new_window'}`
35
+ exec cwd, cmd
36
+ end
37
+
38
+ def tab name, cwd, cmd
39
+ `xdotool key #{configure 'new_tab'}`
40
+ exec cwd, cmd
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,48 @@
1
+ require_relative 'base/base'
2
+
3
+ module TermDump
4
+ # This Terminal class is for [guake](http://guake-project.org/)
5
+ # See `man guake`
6
+ class Terminal < BasicTerminal
7
+ def initialize config
8
+ @user_defined_config = config
9
+ @keybindings = '/apps/guake/keybindings'
10
+ @config = {
11
+ 'new_tab' => get_configure_key('new_tab')
12
+ }
13
+
14
+ @default_config = {
15
+ 'new_window' => 'F12',
16
+ 'new_tab' => 'ctrl+shift+t'
17
+ }
18
+ end
19
+
20
+ def get_configure_key key
21
+ value = IO.popen(
22
+ "gconftool -g '#{@keybindings}/#{key}' 2>/dev/null").read.chomp
23
+ value if value != ''
24
+ end
25
+
26
+ # There is no 'window' concept in guake.
27
+ # Only one terminal instance exists all the time. So treat it as tab
28
+ def window name, cwd, cmd
29
+ tab name, cwd, cmd
30
+ end
31
+
32
+ def tab name, cwd, cmd
33
+ with_name = ''
34
+ with_cwd = ''
35
+ with_cmd = ''
36
+ with_name = "-r #{escape(name)}" unless name.nil? || name == ''
37
+ with_cwd = "-n #{escape(cwd)}" unless cwd.nil? || cwd == ''
38
+ with_cmd = "-e #{escape(cmd)}" unless cmd.nil? || cmd == ''
39
+ `guake #{with_name} #{with_cwd} #{with_cmd}`
40
+ end
41
+
42
+ def exec cwd, cmd
43
+ exec_cmd = "cd #{cwd}"
44
+ exec_cmd += " && #{cmd}" unless cmd.nil? || cmd == ''
45
+ `guake -e #{escape(exec_cmd)}`
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,94 @@
1
+ require_relative 'base/base'
2
+
3
+ module TermDump
4
+ # This Terminal class is for [terminator](http://gnometerminator.blogspot.sg/p/introduction.html)
5
+ # See `man terminator` and `man termintor_config`
6
+ class Terminal < BasicTerminal
7
+ def initialize config
8
+ @user_defined_config = config
9
+ # the config file will be ~/.config/terminator/config,
10
+ # but it may be overridden with $XDG_CONFIG_HOME
11
+ # (in which case it will be $XDG_CONFIG_HOME/terminator/config)
12
+ if ENV['XDG_CONFIG_HOME'].nil?
13
+ configure = "#{Dir.home}/.config/terminator/config"
14
+ else
15
+ configure = "#{ENV['XDG_CONFIG_HOME']}/terminator/config"
16
+ end
17
+ if File.exist?(configure)
18
+ lines = IO.readlines(configure)
19
+ @config = parse_configure lines
20
+ else
21
+ @config = {}
22
+ end
23
+ @default_config = {
24
+ 'new_window' => 'ctrl+shift+i',
25
+ 'new_tab' => 'ctrl+shift+t',
26
+ 'new_vsplit' => 'ctrl+shift+e',
27
+ 'new_hsplit' => 'ctrl+shift+o'
28
+ }
29
+ end
30
+
31
+ CONFIGURE_KEY_MAPPER = {
32
+ 'new_tab' => 'new_tab',
33
+ 'split_vert' => 'new_vsplit',
34
+ 'split_horiz' => 'new_hsplit',
35
+ 'new_window' => 'new_window'
36
+ }
37
+ # Parse lines and fill @config with key mapper result.
38
+ #
39
+ # The configure format of terminator:
40
+ #[keybindings]
41
+ # full_screen = <Ctrl><Shift>F11
42
+ #
43
+ # We only care about the keybindings.
44
+ def parse_configure lines
45
+ config = {}
46
+ in_keybindings = false
47
+ lines.each do |line|
48
+ line.rstrip!
49
+ if in_keybindings
50
+ if line.start_with?('[') || line == ''
51
+ in_keybindings = false
52
+ else
53
+ key, value = line.split('=', 2)
54
+ key.strip!
55
+ first, _, third = value.rpartition('#')
56
+ value = (first != "" ? first.strip : third.strip)
57
+ key = CONFIGURE_KEY_MAPPER[key]
58
+ unless key.nil? || value == ''
59
+ config[key] = convert_key_sequence(value)
60
+ end
61
+ end
62
+ end
63
+ in_keybindings = true if line == '[keybindings]'
64
+ end
65
+ config
66
+ end
67
+
68
+ def exec cwd, cmd
69
+ sleep 0.5
70
+ `xdotool getactivewindow type #{escape("cd #{cwd}\n")}`
71
+ `xdotool getactivewindow type #{escape("#{cmd}\n")}` unless cmd.nil?
72
+ end
73
+
74
+ def window name, cwd, cmd
75
+ `xdotool getactivewindow key #{configure 'new_window'}`
76
+ exec cwd, cmd
77
+ end
78
+
79
+ def tab name, cwd, cmd
80
+ `xdotool getactivewindow key #{configure 'new_tab'}`
81
+ exec cwd, cmd
82
+ end
83
+
84
+ def vsplit name, cwd, cmd
85
+ `xdotool getactivewindow key #{configure 'new_vsplit'}`
86
+ exec cwd, cmd
87
+ end
88
+
89
+ def hsplit name, cwd, cmd
90
+ `xdotool getactivewindow key #{configure 'new_hsplit'}`
91
+ exec cwd, cmd
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'base/base'
2
+
3
+ module TermDump
4
+ # This Terminal class is for xterm
5
+ # See `man xterm`
6
+ class Terminal < BasicTerminal
7
+ def initialize config
8
+ end
9
+
10
+ def exec cwd, cmd
11
+ sleep 0.5
12
+ `xdotool getactivewindow type #{escape("cd #{cwd}\n")}`
13
+ `xdotool getactivewindow type #{escape("#{cmd}\n")}` unless cmd.nil?
14
+ end
15
+
16
+ def window name, cwd, cmd
17
+ IO.popen("xterm &") # `xterm &` is blocking
18
+ exec cwd, cmd
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,4 @@
1
+ module TermDump
2
+ VERSION = '0.2.0'
3
+ end
4
+
data/lib/termdump.rb ADDED
@@ -0,0 +1 @@
1
+ require 'termdump/command'
data/test/run_cmd.sh ADDED
@@ -0,0 +1,8 @@
1
+ #!/bin/sh
2
+
3
+ # test features which can't be tested automatically
4
+ script="../bin/termdump"
5
+ "$script" -s # save and ask for a name
6
+ "$script" -e # edit one
7
+ "$script" # load one
8
+ "$script" -d # delete one
@@ -0,0 +1,32 @@
1
+ require 'minitest/autorun'
2
+
3
+ require 'termdump/terminal/base/base'
4
+
5
+ class TestBasicTerminal < MiniTest::Test
6
+ def test_configure
7
+ bt = TermDump::BasicTerminal.new({'new_window' => 'ctrl+n'})
8
+ assert_raises KeyError do
9
+ bt.configure 'new_tab'
10
+ end
11
+ assert_equal 'ctrl+n', bt.configure('new_window')
12
+ end
13
+
14
+ def test_convert_key_sequence
15
+ bt = TermDump::BasicTerminal.new({})
16
+ assert_equal 'Ctrl+a+Shift', bt.convert_key_sequence('<Ctrl>A<Shift>')
17
+ assert_equal 'Ctrl+Shift+a', bt.convert_key_sequence('<Primary><Shift>A')
18
+ assert_equal 'Alt+space', bt.convert_key_sequence('<Alt>space')
19
+ assert_equal 'Super+plus', bt.convert_key_sequence('<Super>plus')
20
+ assert_equal 'a', bt.convert_key_sequence('A')
21
+ assert_equal 'Shift+a', bt.convert_key_sequence('<Shift>A')
22
+ end
23
+
24
+ def test_escape
25
+ bt = TermDump::BasicTerminal.new({})
26
+ res = IO.popen("echo #{bt.escape("com && this")}").read.chomp
27
+ assert_equal "com && this", res
28
+ res = IO.popen("echo #{bt.escape("com || this")}").read.chomp
29
+ assert_equal "com || this", res
30
+ end
31
+
32
+ end
@@ -0,0 +1,66 @@
1
+ require 'minitest/autorun'
2
+
3
+ require 'termdump/command'
4
+
5
+ class TestCommand < MiniTest::Test
6
+ def setup
7
+ @comm = TermDump::Command.new([])
8
+ end
9
+
10
+ def get_parsed_args args
11
+ TermDump::Command.new(args.split).instance_variable_get(:@args)
12
+ end
13
+
14
+ def test_args
15
+ # list all sessions
16
+ args = get_parsed_args "-l"
17
+ assert_equal true, args.list
18
+ args = get_parsed_args ""
19
+ assert_equal :load, args.action
20
+ assert_equal true, args.list
21
+
22
+ # delete one session
23
+ args = get_parsed_args "-d ruby"
24
+ assert_equal false, args.list
25
+ assert_equal :delete, args.action
26
+ assert_equal 'ruby', args.session
27
+
28
+ # list all sessions and delete one
29
+ args = get_parsed_args "-d"
30
+ assert_equal :delete, args.action
31
+ assert_equal true, args.list
32
+
33
+ # load one session
34
+ args = get_parsed_args "ruby"
35
+ assert_equal :load, args.action
36
+ assert_equal 'ruby', args.session
37
+ assert_equal false, args.list
38
+
39
+ # edit one session
40
+ args = get_parsed_args "-e ruby"
41
+ assert_equal :edit, args.action
42
+ assert_equal 'ruby', args.session
43
+
44
+ # dump to a session
45
+ args = get_parsed_args "-s"
46
+ assert_equal :save, args.action
47
+ assert_equal true, args.list
48
+ args = get_parsed_args "-s ruby"
49
+ assert_equal 'ruby', args.session
50
+
51
+ # print result to stdout(works with -s)
52
+ args = get_parsed_args "-s ruby --stdout"
53
+ assert_equal true, args.stdout
54
+
55
+ # initialize interactively
56
+ args = get_parsed_args "-i"
57
+ assert_equal :init, args.action
58
+
59
+ # dump exclude current tab
60
+ args = get_parsed_args "-s ruby --exclude"
61
+ assert_equal true, args.exclude
62
+ args = get_parsed_args "-s ruby --exclude --stdout"
63
+ assert_equal true, args.stdout
64
+ assert_equal true, args.exclude
65
+ end
66
+ end
@@ -0,0 +1,13 @@
1
+ require 'minitest/autorun'
2
+
3
+ require 'termdump/terminal/gnome_terminal'
4
+
5
+ class TestGnomeTerminal < MiniTest::Test
6
+ @@term = TermDump::Terminal.new({})
7
+
8
+ def test_get_configure_key
9
+ nonexist = @@term.get_configure_key 'nonexist'
10
+ assert_nil nonexist
11
+ # in travi ci, since gconftool does not exist, all key will get empty result
12
+ end
13
+ end
data/test/test_main.rb ADDED
@@ -0,0 +1,241 @@
1
+ require 'minitest/autorun'
2
+ require 'yaml'
3
+
4
+ require 'termdump/main'
5
+
6
+ class TestMain < MiniTest::Test
7
+ def setup
8
+ @main = TermDump::Main.new
9
+ end
10
+
11
+ def test_read_configure
12
+ config = {'terminal' => 'mock', 'new_window' => 'ctrl+n' }
13
+ IO.write "config_fixture", YAML.dump(config)
14
+ TermDump::Main.class_variable_set :@@config_file, "config_fixture"
15
+ main = TermDump::Main.new
16
+ assert_equal config, main.instance_variable_get(:@config)
17
+ File.delete "config_fixture"
18
+ end
19
+
20
+ def test_session_actions
21
+ Dir.rmdir("tmp") if Dir.exist?("tmp")
22
+ Dir.mkdir "tmp"
23
+ path = File.join Dir.pwd, "tmp"
24
+ TermDump::Main.class_variable_set :@@session_dir, path
25
+ status = @main.search_session 'nonexistent'
26
+ assert_equal File.join(path, 'nonexistent.yml'), status[:name]
27
+ assert_equal false, status[:exist]
28
+
29
+ @main.save 'termdump', true, false # print to stdout
30
+ assert_equal 2, Dir.entries(path).size
31
+ # print to stdout and exclude current ptty
32
+ # The output is the same as above, as the parent of `rake test` is `rake`
33
+ # but not the session leader
34
+ @main.save 'termdump', true, true
35
+
36
+ @main.save 'termdump', false, false
37
+ assert_equal 3, Dir.entries(path).size
38
+ status = @main.search_session 'termdump'
39
+ assert_equal true, status[:exist]
40
+ # edit_session can't be tested automatically
41
+ res = @main.load_file 'termdump'
42
+ assert_equal true, res.is_a?(Hash)
43
+
44
+ task = @main.check res # the output of res may be modified
45
+ assert_equal true, task.is_a?(Hash)
46
+
47
+ @main.delete_session 'termdump'
48
+ assert_equal 2, Dir.entries(path).size
49
+ Dir.rmdir "tmp"
50
+ end
51
+
52
+ def test_path
53
+ prefix = @main.exact_commom_prefix(['~/termdump/bin', '~/termdump/lib',
54
+ '~/termdump/test', '~'])
55
+ assert_equal "~/termdump", prefix
56
+ prefix = @main.exact_commom_prefix(['~', '/root', '/root/some'])
57
+ assert_equal "/root", prefix
58
+ prefix = @main.exact_commom_prefix(['~/github/bin', '~/termdump/bin'])
59
+ assert_equal "", prefix
60
+ prefix = @main.exact_commom_prefix(['~/github/bin', '~/termdump/bin',
61
+ '~/termdump/some'])
62
+ assert_equal "", prefix
63
+ end
64
+
65
+ def test_dump
66
+ ptree = {
67
+ '1000' => {
68
+ '1234' => ['~/termdump', TermDump::Process.new('1235', '1234', 'S+', 'vim')],
69
+ '2468' => ['~/github']
70
+ },
71
+ '1111' => {
72
+ '2345' => ['~/termdump', TermDump::Process.new('2346', '2345', 'S+', 'pry')]
73
+ }
74
+ }
75
+ path_prefix = {'$HOME' => '~'}
76
+ expected = { '$HOME' => '~',
77
+ 'window0' => {
78
+ 'cwd' => '~/termdump',
79
+ 'command' => 'vim',
80
+ 'tab0' => {
81
+ 'cwd' => '~/github'
82
+ }
83
+ },
84
+ 'window1' => {
85
+ 'cwd' => '~/termdump',
86
+ 'command' => 'pry'
87
+ }
88
+ }
89
+ assert_equal expected, YAML.load(@main.dump(ptree, path_prefix))
90
+ end
91
+
92
+ def test_parse_variables
93
+ ptree = {
94
+ '$HOME' => '~',
95
+ 'window0' => {
96
+ 'cwd' => '~/${HOME}',
97
+ 'tab0' => {
98
+ 'cwd' => '${HOME}/termdump',
99
+ 'command' => 'vim'
100
+ }
101
+ }
102
+ }
103
+ expected = {
104
+ 'window0' => {
105
+ 'cwd' => "#{Dir.home}/~",
106
+ 'tab0' => {
107
+ 'cwd' => "#{Dir.home}/termdump", 'command' => 'vim'
108
+ }
109
+ }
110
+ }
111
+ assert_equal expected, @main.parse_variables(ptree)
112
+
113
+ # multiple variables
114
+ ptree = {
115
+ '$HOME' => '~',
116
+ '$PROJECT' => 'termdump',
117
+ '$other' => '~/github',
118
+ 'window0' => {
119
+ 'tab0' => { 'cwd' => '${HOME}/${PROJECT}/bin' },
120
+ 'tab1' => { 'cwd' => '${HOME}/${PROJECT}/lib' },
121
+ 'tab2' => { 'cwd' => '${other}'}
122
+ }
123
+ }
124
+ expected = {
125
+ 'window0' => {
126
+ 'tab0' => { 'cwd' => "#{Dir.home}/termdump/bin"},
127
+ 'tab1' => { 'cwd' => "#{Dir.home}/termdump/lib"},
128
+ 'tab2' => { 'cwd' => "#{Dir.home}/github" }
129
+ }
130
+ }
131
+ assert_equal expected, @main.parse_variables(ptree)
132
+
133
+ # escape
134
+ ptree = {
135
+ '$0' => '~/termdump',
136
+ 'window0' => {
137
+ 'tab0' => { 'cwd' => '${0}/bin' },
138
+ 'tab1' => { 'cwd' => '\${0}/lib' },
139
+ }
140
+ }
141
+ expected = {
142
+ 'window0' => {
143
+ 'tab0' => { 'cwd' => "#{Dir.home}/termdump/bin"},
144
+ 'tab1' => { 'cwd' => '\${0}/lib'},
145
+ }
146
+ }
147
+ assert_equal expected, @main.parse_variables(ptree)
148
+ end
149
+
150
+ def test_check_node
151
+ node = {
152
+ 'cwd' => Dir.home,
153
+ 'window0' => { 'cwd' => Dir.home},
154
+ 'tab0' => {
155
+ 'tab0' => {
156
+ 'vsplit' => {
157
+ 'hsplit' => {},
158
+ 'cwd' => Dir.home,
159
+ 'tab0' => {}
160
+ },
161
+ 'hsplit' => {}
162
+ }
163
+ }
164
+ }
165
+ expected = {
166
+ 'cwd' => Dir.home,
167
+ "tab0" => {
168
+ 'cwd' => Dir.home,
169
+ "tab0" => {
170
+ "vsplit" => {
171
+ "hsplit" => {"cwd"=>Dir.home},
172
+ "cwd" => Dir.home
173
+ },
174
+ "hsplit" => {"cwd"=>Dir.home},
175
+ 'cwd' => Dir.home
176
+ }
177
+ }
178
+ }
179
+ assert_equal expected, @main.check_node(node, :tab)
180
+ end
181
+
182
+ def test_cwd_not_exists
183
+ node = {
184
+ 'tab' => {'cwd' => '.'}
185
+ }
186
+ # 'cwd' not found in window
187
+ assert_raises TermDump::SessionSyntaxError do
188
+ @main.check_node node, :window
189
+ end
190
+ # use parent's working directory
191
+ @main.check_node node, :window, Dir.home
192
+ assert_equal Dir.home, node['cwd']
193
+ end
194
+
195
+ def test_can_not_cd_to
196
+ node = {'cwd' => Dir.home}
197
+ @main.check_node node, :window
198
+ pwd = Dir.pwd
199
+ node = {'cwd' => pwd}
200
+ @main.check_node node, :window
201
+ # can't cd to
202
+ assert_raises TermDump::SessionSyntaxError do
203
+ @main.check_node({'cwd' => 'foo'}, :window, pwd)
204
+ end
205
+ end
206
+
207
+ def test_support_relative_path
208
+ node = {'cwd' => '.'}
209
+ pwd = Dir.pwd
210
+ @main.check_node node, :window, pwd
211
+ assert_equal pwd, node['cwd']
212
+ node = {'cwd' => '.'}
213
+ # missing base working directory
214
+ assert_raises TermDump::SessionSyntaxError do
215
+ @main.check_node node, :window
216
+ end
217
+ end
218
+
219
+ def test_node_should_be_hash
220
+ node = { 'tab' => 'cwd' }
221
+ # tab should be a node
222
+ assert_raises TermDump::SessionSyntaxError do
223
+ @main.check_node node, :window
224
+ end
225
+ end
226
+
227
+ def test_cwd_should_be_string
228
+ node = {'cwd' => {}}
229
+ assert_raises TermDump::SessionSyntaxError do
230
+ @main.check_node node, :window
231
+ end
232
+ end
233
+
234
+ def test_command_should_be_string
235
+ node = {'cwd' => Dir.home, 'command' => {}}
236
+ assert_raises TermDump::SessionSyntaxError do
237
+ @main.check_node node, :window
238
+ end
239
+ end
240
+
241
+ end