editserver 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.markdown +3 -2
- data/lib/editserver/command.rb +66 -13
- data/lib/editserver/version.rb +1 -1
- data/lib/editserver.rb +2 -3
- data/test/editserver/command.test.rb +196 -13
- data/test/editserver/socket-test.rb +16 -0
- metadata +5 -7
- data/test/editserver/terminal/emacs.test.rb +0 -5
- data/test/editserver/terminal/vim.test.rb +0 -5
data/.gitignore
CHANGED
data/README.markdown
CHANGED
@@ -20,9 +20,10 @@ Everything works, and core tests are in place. More information forthcoming.
|
|
20
20
|
|
21
21
|
### TODO
|
22
22
|
|
23
|
-
* Finish remaining tests
|
24
23
|
* Improve applescript reliability
|
25
|
-
*
|
24
|
+
* Avoid dynamic creation of Editor subclasses in `Editserver::` namespace
|
25
|
+
(a consequence of an earlier design decision)
|
26
|
+
* Special case: OS X's `Terminal.app` as `editor['terminal']`
|
26
27
|
|
27
28
|
|
28
29
|
[1]: https://chrome.google.com/webstore/detail/ppoadiihggafnhokfkpphojggcdigllp
|
data/lib/editserver/command.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'optparse'
|
2
|
+
require 'fileutils'
|
2
3
|
require 'yaml'
|
3
4
|
require 'webrick/log'
|
4
5
|
require 'rack'
|
@@ -24,6 +25,7 @@ class Editserver
|
|
24
25
|
:AccessLog => [], # rack does its own access logging, so keep this blank
|
25
26
|
:pid => nil,
|
26
27
|
:config => '',
|
28
|
+
:daemonize => false,
|
27
29
|
:environment => 'deployment'
|
28
30
|
}
|
29
31
|
end
|
@@ -38,36 +40,65 @@ class Editserver
|
|
38
40
|
Options:
|
39
41
|
).gsub /^ +/, ''
|
40
42
|
|
41
|
-
opt.on '-
|
43
|
+
opt.on '-H', '--host HOST', "IP/Hostname to bind to; #{rackopts[:Host]} by default" do |arg|
|
44
|
+
@rackopts[:Host] = arg
|
45
|
+
end
|
46
|
+
|
47
|
+
opt.on '-p', '--port NUMBER', Integer, "Port to bind; #{rackopts[:Port]} by default" do |arg|
|
42
48
|
@rackopts[:Port] = arg
|
43
49
|
end
|
44
50
|
|
51
|
+
opt.on '-d', '--default EDITOR', 'Editor to launch at root path; May be one of:',
|
52
|
+
(Editserver.new(editoropts).editors.keys - ['default']).join(', ') do |arg|
|
53
|
+
@editoropts['default'] = arg
|
54
|
+
end
|
55
|
+
|
45
56
|
opt.on '-t', '--terminal CMD', 'Terminal to launch for console editors' do |arg|
|
46
57
|
@editoropts['terminal'] = arg
|
47
58
|
end
|
48
59
|
|
49
|
-
opt.on '
|
50
|
-
|
51
|
-
@
|
60
|
+
opt.on '-f', '--fork', 'Fork and daemonize; returns pid of daemon' do
|
61
|
+
@rackopts[:daemonize] = true
|
62
|
+
@rackopts[:pid] = "/tmp/#{File.basename $0}/#{File.basename $0}.pid"
|
63
|
+
end
|
64
|
+
|
65
|
+
opt.on '-q', '--quiet', 'Produce no output' do
|
66
|
+
@opts[:quiet] = true
|
67
|
+
@rackopts[:Logger] = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL - 1 # zero, essentially
|
68
|
+
@rackopts[:environment] = 'none'
|
69
|
+
end
|
70
|
+
|
71
|
+
opt.on '--rc PATH', "Path to rc file; #{@opts[:rcfile]} by default" do |arg|
|
72
|
+
@rcopts = nil # reset cached user opts
|
52
73
|
@opts[:rcfile] = File.expand_path arg
|
53
74
|
end
|
54
75
|
|
55
76
|
opt.on '--no-rc', 'Suppress reading of rc file' do
|
77
|
+
@rcopts = nil
|
56
78
|
@opts[:norcfile] = true
|
57
79
|
end
|
80
|
+
|
81
|
+
# normally implicit, but must be explicit when having an option beginning with `h'
|
82
|
+
opt.on_tail '-h', '--help' do
|
83
|
+
puts opt; exit
|
84
|
+
end
|
58
85
|
end
|
59
86
|
end
|
60
87
|
|
88
|
+
def say str
|
89
|
+
puts str unless @opts[:quiet]
|
90
|
+
end
|
91
|
+
|
61
92
|
def rcopts
|
62
93
|
@rcopts ||= begin
|
63
94
|
empty = { 'rack' => {}, 'editor' => {} }
|
64
|
-
rcfile = File.expand_path
|
95
|
+
rcfile = File.expand_path @opts[:rcfile]
|
65
96
|
|
66
97
|
if @opts[:norcfile]
|
67
98
|
empty
|
68
99
|
elsif File.exists? rcfile
|
69
100
|
opts = YAML.load_file File.expand_path(rcfile)
|
70
|
-
opts
|
101
|
+
opts = {} unless opts.is_a? Hash
|
71
102
|
opts['rack'] ||= {}
|
72
103
|
opts['editor'] ||= {}
|
73
104
|
opts
|
@@ -103,13 +134,30 @@ class Editserver
|
|
103
134
|
|
104
135
|
def run
|
105
136
|
options.parse @args
|
137
|
+
$0 = 'editserver'
|
138
|
+
|
139
|
+
# Rack::Server issues shutdown on SIGINT only
|
140
|
+
trap :TERM do
|
141
|
+
trap :TERM, 'DEFAULT'
|
142
|
+
Process.kill :INT, $$
|
143
|
+
end
|
106
144
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
145
|
+
if rackopts[:daemonize]
|
146
|
+
FileUtils.mkdir_p File.dirname(rackopts[:pid])
|
147
|
+
Process.wait fork { server.start }
|
148
|
+
sleep 0.1 until File.exists? rackopts[:pid] and File.read(rackopts[:pid]).to_i > 0
|
149
|
+
|
150
|
+
say host_and_port
|
151
|
+
say "Editserver PID: #{fx File.read(rackopts[:pid]), [36,1]}"
|
152
|
+
else
|
153
|
+
begin
|
154
|
+
say banner
|
155
|
+
server.start
|
156
|
+
say fx("\nGoodbye!", [32,1])
|
157
|
+
rescue StandardError => e
|
158
|
+
say fx(e.to_s, [31,1])
|
159
|
+
exit e.respond_to?(:errno) ? e.errno : 1
|
160
|
+
end
|
113
161
|
end
|
114
162
|
end
|
115
163
|
|
@@ -125,10 +173,15 @@ class Editserver
|
|
125
173
|
\\ \\____\\ \\___,_\\ \\_\\ \\__\\/\\____/\\ \\____\\\\ \\_\\ \\ \\___/ \\ \\____\\\\ \\_\\
|
126
174
|
\\/____/\\/__,_ /\\/_/\\/__/\\/___/ \\/____/ \\/_/ \\/__/ \\/____/ \\/_/
|
127
175
|
|
128
|
-
|
176
|
+
#{host_and_port}
|
177
|
+
Press #{fx 'Ctrl-C', [36,1]} to exit.\
|
129
178
|
).gsub(/^ {8}/, '')
|
130
179
|
end
|
131
180
|
|
181
|
+
def host_and_port
|
182
|
+
"Listening on #{fx "#{rackopts[:Host]}:#{rackopts[:Port]}", [32,1]}"
|
183
|
+
end
|
184
|
+
|
132
185
|
def fx str, effects = []
|
133
186
|
return str unless $stdout.tty?
|
134
187
|
str.gsub /^(.*)$/, "\e[#{[effects].flatten.join ';'}m\\1\e[0m"
|
data/lib/editserver/version.rb
CHANGED
data/lib/editserver.rb
CHANGED
@@ -12,9 +12,8 @@ class Editserver
|
|
12
12
|
# OS X editors
|
13
13
|
'mate' => 'mate -w',
|
14
14
|
'mvim' => 'mvim --nofork --servername EDITSERVER', # does not return when app must be launched
|
15
|
-
'kod' => 'open -a Kod -W', # app must quit to release control
|
16
15
|
'bbedit' => 'bbedit -w' # does not open file properly when app is launched
|
17
|
-
}
|
16
|
+
}.reject { |k,v| not File.executable? %x(which #{k.shellsplit[0]}).chomp }
|
18
17
|
|
19
18
|
attr_reader :editors
|
20
19
|
|
@@ -78,8 +77,8 @@ class Editserver
|
|
78
77
|
klass = editor request.path_info
|
79
78
|
Response.new(klass.new, request).call
|
80
79
|
rescue RoutingError => e
|
81
|
-
warn e.to_s
|
82
80
|
res = Rack::Response.new
|
81
|
+
res.write e.to_s
|
83
82
|
res.status = 500
|
84
83
|
res.finish
|
85
84
|
end
|
@@ -1,37 +1,57 @@
|
|
1
1
|
$:.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
$:.unshift File.dirname(__FILE__)
|
2
3
|
|
4
|
+
require 'tempfile'
|
5
|
+
require 'yaml'
|
6
|
+
require 'webrick/log'
|
7
|
+
require 'rack/server'
|
3
8
|
require 'editserver/command'
|
4
9
|
require 'minitest/pride' if $stdout.tty?
|
5
10
|
require 'minitest/autorun'
|
11
|
+
require 'socket-test'
|
6
12
|
|
7
13
|
describe Editserver::Command do
|
14
|
+
before { @cmd = Editserver::Command.new ['--no-rc'] }
|
15
|
+
|
8
16
|
describe :initialize do
|
9
17
|
it 'should take a single optional argument' do
|
10
18
|
Editserver::Command.method(:initialize).arity.must_equal -1
|
11
19
|
end
|
12
20
|
|
13
21
|
it 'should set internal state' do
|
14
|
-
cmd
|
15
|
-
cmd.instance_variable_get(:@
|
16
|
-
cmd.instance_variable_get(:@
|
17
|
-
cmd.instance_variable_get(:@
|
18
|
-
|
19
|
-
|
20
|
-
].sort_by &:to_s
|
22
|
+
@cmd.instance_variable_get(:@args).must_equal ['--no-rc']
|
23
|
+
@cmd.instance_variable_get(:@opts).must_equal(:rcfile => '~/.editserverrc')
|
24
|
+
@cmd.instance_variable_get(:@editoropts).must_equal('default' => nil, 'terminal' => nil)
|
25
|
+
@cmd.instance_variable_get(:@rackopts).keys.sort_by(&:to_s).must_equal [
|
26
|
+
:Host, :Port, :Logger, :AccessLog, :pid, :config, :daemonize, :environment
|
27
|
+
].sort_by(&:to_s)
|
21
28
|
end
|
22
29
|
end
|
23
30
|
|
24
31
|
describe :options do
|
25
|
-
before { @cmd = Editserver::Command.new ['--no-rc'] }
|
26
|
-
|
27
32
|
it 'should return an OptionParser object' do
|
28
33
|
@cmd.options.must_be_kind_of OptionParser
|
29
34
|
end
|
30
35
|
|
31
36
|
it 'should modify internal state when parsing a list of arguments' do
|
37
|
+
@cmd.options.parse %w[--host 0.0.0.0]
|
38
|
+
@cmd.instance_variable_get(:@rackopts)[:Host].must_equal '0.0.0.0'
|
39
|
+
|
32
40
|
@cmd.options.parse %w[--port 1000]
|
33
41
|
@cmd.instance_variable_get(:@rackopts)[:Port].must_equal 1000
|
34
42
|
|
43
|
+
@cmd.options.parse %w[--fork]
|
44
|
+
@cmd.instance_variable_get(:@rackopts)[:daemonize].must_equal true
|
45
|
+
@cmd.instance_variable_get(:@rackopts)[:pid].must_equal "/tmp/#{File.basename $0}/#{File.basename $0}.pid"
|
46
|
+
|
47
|
+
@cmd.options.parse %w[--quiet]
|
48
|
+
@cmd.instance_variable_get(:@opts)[:quiet].must_equal true
|
49
|
+
@cmd.instance_variable_get(:@rackopts)[:Logger].level.must_equal WEBrick::BasicLog::FATAL - 1
|
50
|
+
@cmd.instance_variable_get(:@rackopts)[:environment].must_equal 'none'
|
51
|
+
|
52
|
+
@cmd.options.parse %w[--default mate]
|
53
|
+
@cmd.instance_variable_get(:@editoropts)['default'].must_equal 'mate'
|
54
|
+
|
35
55
|
@cmd.options.parse %w[--terminal xterm]
|
36
56
|
@cmd.instance_variable_get(:@editoropts)['terminal'].must_equal 'xterm'
|
37
57
|
|
@@ -43,22 +63,185 @@ describe Editserver::Command do
|
|
43
63
|
end
|
44
64
|
end
|
45
65
|
|
46
|
-
|
47
|
-
|
48
|
-
|
66
|
+
describe :say do
|
67
|
+
it 'should write to $stdout unless --quiet option is specified' do
|
68
|
+
capture_io { @cmd.say 'AHHHHHH!' }.first.must_equal "AHHHHHH!\n"
|
69
|
+
@cmd.instance_variable_get(:@opts)[:quiet] = true
|
70
|
+
capture_io { @cmd.say 'AHHHHHH!' }.first.must_equal ''
|
71
|
+
end
|
72
|
+
end
|
49
73
|
|
50
74
|
describe :rcopts do
|
51
|
-
|
75
|
+
before do
|
76
|
+
@rcfile = Tempfile.new 'editserverrc'
|
77
|
+
@cmd.instance_variable_get(:@opts)[:rcfile] = @rcfile.path
|
78
|
+
end
|
79
|
+
|
80
|
+
after { (@rcfile.close; @rcfile.unlink) if @rcfile.path }
|
81
|
+
|
82
|
+
it 'should load the YAML file specified at @opts[:rcfile]' do
|
83
|
+
opts = { 'editor' => { 'default' => 'emacs', 'terminal' => 'xterm' }, 'rack' => { 'port' => 1000 } }
|
84
|
+
@rcfile.write opts.to_yaml
|
85
|
+
@rcfile.rewind
|
86
|
+
@cmd.rcopts.must_equal opts
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should create any missing top level keys' do
|
90
|
+
@rcfile.write({ 'foo' => 'bar' }.to_yaml)
|
91
|
+
@rcfile.rewind
|
92
|
+
@cmd.rcopts.must_equal 'foo' => 'bar', 'rack' => {}, 'editor' => {}
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should return bare options if --no-rc is specified' do
|
96
|
+
opts = { 'editor' => { 'default' => 'emacs', 'terminal' => 'xterm' }, 'rack' => { 'port' => 1000 } }
|
97
|
+
@rcfile.write opts.to_yaml
|
98
|
+
@rcfile.rewind
|
99
|
+
@cmd.instance_variable_get(:@opts)[:norcfile] = true
|
100
|
+
@cmd.rcopts.must_equal 'rack' => {}, 'editor' => {}
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should return bare options if rcfile does not exist' do
|
104
|
+
opts = { 'editor' => { 'default' => 'emacs', 'terminal' => 'xterm' }, 'rack' => { 'port' => 1000 } }
|
105
|
+
@rcfile.write opts.to_yaml
|
106
|
+
@rcfile.rewind
|
107
|
+
@rcfile.close
|
108
|
+
@rcfile.unlink
|
109
|
+
@cmd.rcopts.must_equal 'rack' => {}, 'editor' => {}
|
110
|
+
end
|
111
|
+
end # rcopts
|
52
112
|
|
53
113
|
describe :rackopts do
|
114
|
+
it 'should return @rackopts masked with @rcopts' do
|
115
|
+
@cmd.instance_variable_get(:@opts)[:rcfile] = '/dev/null'
|
116
|
+
@cmd.rackopts[:Port].must_equal 9999
|
117
|
+
@cmd.rackopts[:Host].must_equal '127.0.0.1'
|
118
|
+
@cmd.instance_variable_get(:@rcopts).merge!('rack' => { 'port' => 65535, 'host' => '1.1.1.1' })
|
119
|
+
@cmd.rackopts[:Port].must_equal 65535
|
120
|
+
@cmd.rackopts[:Host].must_equal '1.1.1.1'
|
121
|
+
end
|
54
122
|
end
|
55
123
|
|
56
124
|
describe :editoropts do
|
125
|
+
it 'should return @editoropts masked with @rcopts' do
|
126
|
+
@cmd.instance_variable_get(:@opts)[:rcfile] = '/dev/null'
|
127
|
+
@cmd.editoropts['default'].must_equal nil
|
128
|
+
@cmd.editoropts['terminal'].must_equal nil
|
129
|
+
@cmd.instance_variable_get(:@rcopts).merge!('editor' => { 'default' => 'pony', 'terminal' => 'sparkles' })
|
130
|
+
@cmd.editoropts['default'].must_equal 'pony'
|
131
|
+
@cmd.editoropts['terminal'].must_equal 'sparkles'
|
132
|
+
end
|
57
133
|
end
|
58
134
|
|
59
135
|
describe :server do
|
136
|
+
it 'should return an instance of Rack::Server' do
|
137
|
+
@cmd.server.must_be_kind_of Rack::Server
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should pass rack options to new server instance' do
|
141
|
+
@cmd.rcopts['rack']['port'] = 4000
|
142
|
+
@cmd.server.options[:Port].must_equal 4000
|
143
|
+
@cmd.rcopts['rack']['pid'] = '/dev/null'
|
144
|
+
@cmd.server.options[:pid].must_equal '/dev/null'
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should set the server's @app to an instance of Editserver" do
|
148
|
+
@cmd.server.app.must_be_kind_of Editserver
|
149
|
+
end
|
60
150
|
end
|
61
151
|
|
62
152
|
describe :run do
|
153
|
+
it 'should parse the arguments stored in @args' do
|
154
|
+
@cmd.instance_variable_set :@args, ['--help']
|
155
|
+
|
156
|
+
rd, wr = IO.pipe
|
157
|
+
|
158
|
+
pid = fork do
|
159
|
+
rd.close
|
160
|
+
$stdout.reopen wr
|
161
|
+
@cmd.run
|
162
|
+
end
|
163
|
+
|
164
|
+
wr.close
|
165
|
+
Process.wait2(pid).last.exitstatus.must_equal 0
|
166
|
+
rd.read.must_match /Usage:.*--help/m
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'should start the server, and shutdown on SIGINT and SIGTERM' do
|
170
|
+
# thread, because we don't want to serially boot two servers
|
171
|
+
pool = []
|
172
|
+
[[:INT, 10000], [:TERM, 10001]].each do |sig, port|
|
173
|
+
pool << Thread.new do
|
174
|
+
cmd = @cmd.dup
|
175
|
+
rd, wr = IO.pipe
|
176
|
+
|
177
|
+
cmd.instance_variable_get(:@rackopts)[:Port] = port
|
178
|
+
|
179
|
+
pid = fork do
|
180
|
+
rd.close
|
181
|
+
$stdout.reopen wr
|
182
|
+
cmd.run
|
183
|
+
end
|
184
|
+
|
185
|
+
wr.close
|
186
|
+
|
187
|
+
sleep 0.1 until SocketTest.open? cmd.rackopts[:Host], port
|
188
|
+
|
189
|
+
Process.kill sig, pid
|
190
|
+
|
191
|
+
# give the server some time to shutdown
|
192
|
+
tries = 0
|
193
|
+
until Process.wait pid, Process::WNOHANG
|
194
|
+
sleep 0.1
|
195
|
+
# but no more than 2 seconds
|
196
|
+
Process.kill :KILL, pid if (tries += 1) > 20
|
197
|
+
end
|
198
|
+
|
199
|
+
(tries <= 20).must_equal true
|
200
|
+
rd.read.must_match /Listening.*#{cmd.rackopts[:Host]}:#{port}/
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
pool.each &:join
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should daemonize and write pidfile in daemon mode' do
|
208
|
+
@cmd.options.parse ['--fork', '--port=10002']
|
209
|
+
|
210
|
+
begin
|
211
|
+
bgpid = fork do
|
212
|
+
$stdout.reopen '/dev/null' # we're not too interested in this output
|
213
|
+
@cmd.run
|
214
|
+
end
|
215
|
+
|
216
|
+
sleep 0.1 until SocketTest.open? @cmd.rackopts[:Host], @cmd.rackopts[:Port]
|
217
|
+
Process.wait bgpid
|
218
|
+
|
219
|
+
pidfile = @cmd.rackopts[:pid]
|
220
|
+
pidbuf = File.read pidfile
|
221
|
+
pid = pidbuf.to_i
|
222
|
+
|
223
|
+
pidbuf.must_match /\A\d+\z/ # does it look like a pid?
|
224
|
+
Process.kill(0, pid).must_equal 1 # is it really alive?
|
225
|
+
ensure
|
226
|
+
# make sure the sucker dies
|
227
|
+
Process.kill :INT, pid
|
228
|
+
tries = 0
|
229
|
+
loop do
|
230
|
+
begin
|
231
|
+
Process.kill 0, pid
|
232
|
+
sleep 0.1
|
233
|
+
rescue Errno::ESRCH
|
234
|
+
break
|
235
|
+
end
|
236
|
+
if (tries += 1) > 20
|
237
|
+
Process.kill :KILL, pid
|
238
|
+
break
|
239
|
+
end
|
240
|
+
end
|
241
|
+
(tries <= 20).must_equal true
|
242
|
+
end
|
243
|
+
|
244
|
+
File.exists?(@cmd.rackopts[:pid]).must_equal false
|
245
|
+
end
|
63
246
|
end
|
64
247
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module SocketTest
|
4
|
+
class << self
|
5
|
+
include Socket::Constants
|
6
|
+
|
7
|
+
def open? host, port
|
8
|
+
sock = Socket.new AF_INET, SOCK_STREAM, 0
|
9
|
+
addr = Socket.sockaddr_in port, host
|
10
|
+
sock.connect addr
|
11
|
+
true
|
12
|
+
rescue Errno::ECONNREFUSED
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 4
|
9
|
+
version: 0.1.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Sung Pae
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-02-
|
17
|
+
date: 2011-02-17 00:00:00 -06:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -86,8 +86,7 @@ files:
|
|
86
86
|
- test/editserver/command.test.rb
|
87
87
|
- test/editserver/editor.test.rb
|
88
88
|
- test/editserver/response.test.rb
|
89
|
-
- test/editserver/
|
90
|
-
- test/editserver/terminal/vim.test.rb
|
89
|
+
- test/editserver/socket-test.rb
|
91
90
|
- test/test-editor
|
92
91
|
has_rdoc: true
|
93
92
|
homepage: http://github.com/guns/editserver
|
@@ -126,6 +125,5 @@ test_files:
|
|
126
125
|
- test/editserver/command.test.rb
|
127
126
|
- test/editserver/editor.test.rb
|
128
127
|
- test/editserver/response.test.rb
|
129
|
-
- test/editserver/
|
130
|
-
- test/editserver/terminal/vim.test.rb
|
128
|
+
- test/editserver/socket-test.rb
|
131
129
|
- test/test-editor
|