termdump 0.2.0

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.
@@ -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