nvim 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,142 @@
1
+ #
2
+ # neovim/output.rb -- Output to Neovim
3
+ #
4
+
5
+
6
+ module Neovim
7
+
8
+ class Write
9
+ class <<self
10
+ def open *args, **kwargs
11
+ i = new *args, **kwargs
12
+ yield i
13
+ ensure
14
+ i.finish
15
+ end
16
+ end
17
+ def initialize client, *rest
18
+ @client = client
19
+ end
20
+ def << arg
21
+ write arg.to_s
22
+ self
23
+ end
24
+ def print *args
25
+ args.each { |a| write a.to_s }
26
+ nil
27
+ end
28
+ def puts *args
29
+ if args.empty? then
30
+ write $/
31
+ else
32
+ args.each { |a|
33
+ case a
34
+ when Array then
35
+ a.each { |e|
36
+ puts e
37
+ }
38
+ else
39
+ a = a.to_s
40
+ write a
41
+ write $/ unless a.end_with? $/
42
+ end
43
+ }
44
+ end
45
+ nil
46
+ end
47
+ def flush
48
+ end
49
+ end
50
+
51
+ class WriteStd < Write
52
+ class <<self
53
+ def redirect *args, **kwargs
54
+ open *args, **kwargs do |i|
55
+ old, $stdout = $stdout, i
56
+ yield
57
+ ensure
58
+ $stdout = old
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ class WriteOut < WriteStd
65
+ def write *args
66
+ args.each { |a|
67
+ a = a.to_s
68
+ a.notempty? or next
69
+ @client.out_write a
70
+ @line_open = !(a.end_with? $/)
71
+ }
72
+ nil
73
+ end
74
+ def finish
75
+ if @line_open then
76
+ @client.out_write $/
77
+ @line_open = nil
78
+ end
79
+ end
80
+ end
81
+
82
+ class WriteErr < Write
83
+ class <<self
84
+ def redirect *args, **kwargs
85
+ open *args, **kwargs do |i|
86
+ old, $stderr = $stderr, i
87
+ yield
88
+ ensure
89
+ $stderr = old
90
+ end
91
+ end
92
+ end
93
+ def write *args
94
+ args.each { |a|
95
+ @rest ||= ""
96
+ @rest << a.to_s
97
+ while @rest =~ /#$// do
98
+ @client.err_writeln $`
99
+ @rest = $'
100
+ end
101
+ }
102
+ nil
103
+ end
104
+ def finish
105
+ if @rest.notempty? then
106
+ @client.err_writeln @rest
107
+ @rest = nil
108
+ end
109
+ end
110
+ end
111
+
112
+ class WriteBuf < WriteStd
113
+ def initialize *args, whole: nil, top: nil
114
+ super
115
+ @lines, @last = [], ""
116
+ @whole, @top = whole, top
117
+ end
118
+ def write *args
119
+ args.each { |a| @last << a.to_s }
120
+ loop do
121
+ n, r = @last.split $/, 2
122
+ r or break
123
+ @lines.push n
124
+ @last = r
125
+ end
126
+ nil
127
+ end
128
+ def finish
129
+ if @last.notempty? then
130
+ @lines.push @last
131
+ @last = nil
132
+ end
133
+ if @whole then
134
+ @client.buf_set_lines 0, 0, -1, true, @lines
135
+ else
136
+ @client.put @lines, "l", true, !@top
137
+ end
138
+ end
139
+ end
140
+
141
+ end
142
+
data/lib/neovim/remote.rb CHANGED
@@ -1,53 +1,276 @@
1
1
  #
2
- # neovim/remote.rb -- Host for Neovim
2
+ # neovim/remote.rb -- Remote access for Neovim
3
3
  #
4
4
 
5
- require "neovim/session"
6
- require "neovim/handler"
5
+ require "neovim/foreign/supplement"
6
+
7
+ require "neovim/logging"
8
+ require "neovim/connection"
7
9
 
8
10
 
9
11
  module Neovim
10
12
 
11
- class Remote < Session
13
+ class Remote
14
+
15
+ class Message
16
+
17
+ @subs, @subh = [], {}
18
+
19
+ class <<self
20
+
21
+ def from_array ary
22
+ kind, *payload = *ary
23
+ klass = find kind
24
+ klass or raise "No message type for id #{kind.inspect}"
25
+ klass[ *payload]
26
+ end
27
+
28
+ def inherited cls ; @subs.push cls ; end
29
+ def find id
30
+ @subh[ id] ||= @subs.find { |c| c::ID == id }
31
+ end
32
+
33
+ alias [] new
34
+
35
+ end
36
+
37
+ def initialize *args
38
+ z = self.class::KEYS.zip args
39
+ @cont = z.inject Hash.new do |c,(h,k)| c[h] = k ; c end
40
+ end
41
+
42
+ def inspect
43
+ "#<#{self.class.plain_name} #@cont>"
44
+ end
45
+
46
+ def to_s
47
+ j = @cont.map { |k,v| "#{k}:#{v}" if v }.compact.join ","
48
+ "#{self.class.plain_name}(#{j})"
49
+ end
50
+
51
+ def method_missing sym, *args
52
+ if @cont.key? sym then @cont[ sym] else super end
53
+ end
54
+
55
+ def respond_to_missing? sym, priv = nil
56
+ @cont.key? sym.to_sym
57
+ end
58
+
59
+ def methods *args
60
+ super.concat @cont.keys
61
+ end
62
+
63
+ def to_h ; @cont ; end
64
+
65
+ def fields ; @cont.fetch_values *self.class::KEYS ; end
66
+
67
+ def to_a
68
+ [self.class::ID, *fields]
69
+ end
70
+
71
+ class Request < Message
72
+ ID = 0
73
+ KEYS = %i(request_id method_name arguments)
74
+ end
75
+
76
+ class Response < Message
77
+ ID = 1
78
+ KEYS = %i(request_id error value)
79
+ def initialize *args
80
+ super
81
+ e = @cont[ :error]
82
+ if e and not Array === e then
83
+ @cont[ :error] = [0, e]
84
+ end
85
+ end
86
+ end
87
+
88
+ class Notification < Message
89
+ ID = 2
90
+ KEYS = %i(method_name arguments)
91
+ end
92
+
93
+ end
94
+
95
+ class ResponseError < StandardError ; end
96
+
97
+ class Disconnected < RuntimeError ; end
98
+
99
+
100
+ include Logging
12
101
 
13
102
  class <<self
14
103
 
104
+ include Logging
105
+
106
+ private :new
107
+
108
+ def open_conn conntype, *args, **kwargs
109
+ conntype.open_files *args, **kwargs do |conn|
110
+ yield conn
111
+ end
112
+ end
113
+
114
+ public
115
+
116
+ def open conntype, *args, **kwargs
117
+ open_conn conntype, *args, **kwargs do |conn|
118
+ i = new nil, conn
119
+ yield i
120
+ end
121
+ end
122
+
123
+ def start plugins, *args
124
+ open_logfile do
125
+ log :info, "Starting", args: $*
126
+ open_conn *args do |conn|
127
+ i = new plugins, conn
128
+ yield i
129
+ end
130
+ ensure
131
+ log :info, "Leaving"
132
+ end
133
+ end
134
+
15
135
  def start_client *args
16
- start *args do |i|
136
+ start nil, *args do |i|
17
137
  yield i.start
18
138
  end
19
139
  end
20
140
 
21
141
  end
22
142
 
23
- def initialize conn
24
- super
143
+ def initialize plugins, conn
144
+ @conn = conn
145
+ @request_id = 0
146
+ @responses = {}
25
147
  @plugins = {}
148
+ @plugins.update plugins if plugins
26
149
  end
27
150
 
151
+ def client_name
152
+ l = @plugins.values.select { |p| p.type }
153
+ if l.notempty? then
154
+ l.map! { |p| p.type }
155
+ l.uniq!
156
+ name = l.join "-"
157
+ log :info, "Client Name", name: name
158
+ "ruby-#{name}-host"
159
+ else
160
+ "ruby-client"
161
+ end
162
+ end
163
+
164
+ def client_type ; self.class.plain_name.downcase ; end
165
+
166
+ def client_methods
167
+ l = @plugins.values.reject { |p| p.type }
168
+ if l.notempty? then
169
+ r = {}
170
+ l.each { |p| p.options { |name,opts| r[ name] = opts } }
171
+ r
172
+ end
173
+ end
174
+
175
+
28
176
  def start
29
- @conn.start @comm, client_name, self.class.name.downcase.to_sym, client_methods
177
+ @conn.start self
30
178
  @conn.client
31
179
  end
32
180
 
33
- def client_name ; "ruby-client" ; end
34
- def client_methods ; end
181
+ def run until_id = nil
182
+ loop do
183
+ if @deferred and @conn.client then
184
+ d, @deferred = @deferred, nil
185
+ d.each { |p| p.call }
186
+ end
187
+ message = get
188
+ case message
189
+ when Message::Response then
190
+ if @responses.key? message.request_id then
191
+ @responses[ message.request_id] = message
192
+ else
193
+ log :warning, "Dropped response", message.request_id
194
+ end
195
+ when Message::Request, Message::Notification then
196
+ h = find_handler message.method_name
197
+ if h then
198
+ p = proc do
199
+ begin
200
+ log :debug1, "Calling handler", name: message.method_name, args: message.arguments
201
+ r = h.execute @conn.client, *message.arguments
202
+ log :debug1, "Handler result", result: r
203
+ rescue
204
+ e = [ 0, $!.to_s]
205
+ log_exception :error
206
+ end
207
+ put Message::Response[ message.request_id, e, r] if message.respond_to? :request_id
208
+ end
209
+ if @conn.client or not h.needs_client? then
210
+ p.call
211
+ else
212
+ log :info, "Deferred handler for", name: message.method_name
213
+ @deferred ||= []
214
+ @deferred.push p
215
+ end
216
+ else
217
+ if message.respond_to? :request_id then
218
+ put Message::Response[ message.request_id, [0, "No handler #{message.method_name}."], nil]
219
+ end
220
+ end
221
+ end
222
+ break if until_id and @responses[ until_id]
223
+ end
224
+ end
225
+
226
+ def request method, *args
227
+ rid = @request_id = @request_id.succ
228
+ put Message::Request[ rid, method, args]
229
+ @responses[ rid] = nil
230
+ run rid
231
+ r = @responses.delete rid
232
+ if r.error then
233
+ t, e = *r.error
234
+ t = @conn.error t
235
+ raise ResponseError, "#{t}: #{e}"
236
+ end
237
+ r.value
238
+ end
35
239
 
240
+ def notify method, *args
241
+ put Message::Notification[ method, args]
242
+ end
243
+
244
+ private
245
+
246
+ def put msg
247
+ log :debug2, "Sending Message", data: msg
248
+ @conn.put msg.to_a
249
+ self
250
+ rescue Errno::EPIPE
251
+ raise Disconnected, "Broken pipe on write"
252
+ end
36
253
 
37
- def add_plugins source, plugins
38
- @plugins[ source] = plugins
254
+ def get
255
+ IO.select [@conn.input], nil, nil
256
+ raise Disconnected, "EOF on wait" if @conn.eof?
257
+ msg = Message.from_array @conn.get
258
+ log :debug2, "Received Message", data: msg
259
+ msg
260
+ rescue EOFError
261
+ raise Disconnected, "EOF on read"
39
262
  end
40
263
 
41
- def execute_handler name, args
264
+ def find_handler name
42
265
  @plugins.each_value do |plugin|
43
- handler = plugin.get_handler name
44
- if handler then
266
+ h = plugin.get_handler name
267
+ if h then
45
268
  log :info, "Found handler", name: name
46
- log :debug1, "Calling with", args: args
47
- return handler.execute @conn.client, *args
269
+ return h
48
270
  end
49
271
  end
50
- super
272
+ log :error, "No handler found for #{name}."
273
+ nil
51
274
  end
52
275
 
53
276
  end
@@ -55,7 +55,7 @@ module Neovim
55
55
  end
56
56
 
57
57
  def type
58
- @type ||= (name.sub /.*::/, "").to_sym
58
+ @type ||= plain_name.to_sym
59
59
  end
60
60
 
61
61
  end
@@ -231,7 +231,7 @@ module Neovim
231
231
  def line_indices pos, len
232
232
  if Range === pos then
233
233
  r = pos
234
- pos, lst = r.begin, r.end
234
+ pos, lst = r.begin||1, r.end||-1
235
235
  lst += 1 unless r.exclude_end?
236
236
  elsif pos.nil? then
237
237
  pos, lst = 1, 0
@@ -241,7 +241,7 @@ module Neovim
241
241
  if len then
242
242
  lst = pos + (len >= 0 ? len : 0)
243
243
  end
244
- lst = 0 if pos < 0 and lst >= 0
244
+ lst = 0 if pos < 0 and lst > 0
245
245
  yield pos-1, lst-1
246
246
  end
247
247
 
@@ -249,11 +249,11 @@ module Neovim
249
249
  line_indices pos, len do |*fl|
250
250
  c = nil
251
251
  fl.map! { |y|
252
- if y >= 0 then
253
- y
254
- else
255
- y + 1 + (c ||= count)
252
+ unless y >= 0 then
253
+ c ||= count
254
+ y += 1 + c
256
255
  end
256
+ y
257
257
  }
258
258
  yield *fl
259
259
  end
@@ -4,6 +4,7 @@
4
4
 
5
5
  require "neovim/handler"
6
6
  require "neovim/remote_object"
7
+ require "neovim/output"
7
8
 
8
9
 
9
10
  class Object
@@ -51,118 +52,14 @@ end
51
52
 
52
53
  module Neovim
53
54
 
54
- class Write
55
- class <<self
56
- def open client
57
- i = new client
58
- yield i
59
- ensure
60
- i.finish
61
- end
62
- end
63
- def initialize client
64
- @client = client
65
- end
66
- def print *args
67
- args.each { |a| write a.to_s }
68
- nil
69
- end
70
- def puts *args
71
- args.each { |a|
72
- a = a.to_s
73
- write a
74
- write "\n" unless a.end_with? $/
75
- }
76
- nil
77
- end
78
- def flush
79
- end
80
- end
81
-
82
- class WriteStd < Write
83
- class <<self
84
- def redirect client
85
- open client do |i|
86
- old, $stdout = $stdout, i
87
- yield
88
- ensure
89
- $stdout = old
90
- end
91
- end
92
- end
93
- end
94
-
95
- class WriteOut < WriteStd
96
- def write *args
97
- args.each { |a|
98
- a.notempty? or next
99
- @client.out_write a
100
- @line_open = !(a.end_with? $/)
101
- }
102
- nil
103
- end
104
- def finish
105
- if @line_open then
106
- @client.out_write $/
107
- @line_open = nil
108
- end
109
- end
110
- end
111
-
112
- class WriteErr < Write
113
- class <<self
114
- def redirect client
115
- open client do |i|
116
- old, $stderr = $stderr, i
117
- yield
118
- ensure
119
- $stderr = old
120
- end
121
- end
122
- end
123
- def write *args
124
- args.each { |a|
125
- @rest ||= ""
126
- @rest << a
127
- while @rest =~ /#$// do
128
- @client.err_writeln $`
129
- @rest = $'
130
- end
131
- }
132
- nil
133
- end
134
- def finish
135
- if @rest.notempty? then
136
- @client.err_writeln @rest
137
- @rest = nil
138
- end
139
- end
140
- end
141
-
142
- class WriteBuf < WriteStd
143
- def write *args
144
- s = @rest||""
145
- args.each { |a|
146
- s << a
147
- }
148
- s = s.split $/, -1
149
- @rest = s.pop
150
- @client.put s, "l", true, false
151
- nil
152
- end
153
- def finish
154
- if @rest.notempty? then
155
- @client.put [@rest], "l", true, false
156
- @rest = nil
157
- end
158
- end
159
- end
160
-
161
-
162
55
  class DslProvider < DslBase
163
56
 
164
57
  TYPE = :script
165
58
 
59
+ def initialize source
60
+ super *[]
61
+ end
62
+
166
63
  def setup &block
167
64
  add_setup_block &block
168
65
  end
@@ -272,6 +169,21 @@ module Neovim
272
169
  plugin_provider do |dsl|
273
170
 
274
171
  dsl.setup do |client|
172
+ r = client.get_var "ruby_require" rescue nil
173
+ case r
174
+ when Array then r = r.notempty?
175
+ when nil then nil
176
+ else r = [r]
177
+ end
178
+ if r then
179
+ WriteOut.redirect client do # Protect the RPC interface against erroneous output.
180
+ r.each do |l|
181
+ require l
182
+ rescue LoadError
183
+ client.err_writeln + $!.to_s
184
+ end
185
+ end
186
+ end
275
187
  $curbuf = client.get_current_buf
276
188
  $curwin = client.get_current_win
277
189
  end
@@ -301,10 +213,13 @@ module Neovim
301
213
  if !code.notempty? or code == "|" then # Workaround because Neovim doesn't allow empty code (the ultimate Quine)
302
214
  set_global_client client do
303
215
  client.command "#{lst}"
304
- code = (get_lines client, fst..lst).join "\n"
216
+ code = (get_lines client, fst..lst).join $/
305
217
  WriteBuf.redirect client do
306
218
  r = script_binding.eval code, "ruby_run"
307
- r.nil? or puts "#=> #{r.inspect}"
219
+ unless r.nil? then
220
+ script_binding.local_variable_set :_, r
221
+ puts "#=> #{r.inspect}"
222
+ end
308
223
  end
309
224
  end
310
225
  elsif code == "+" then
@@ -0,0 +1,78 @@
1
+ #
2
+ # neovim/tools/copy.rb -- Set X11 selection and Tmux buffer
3
+ #
4
+
5
+
6
+ module Kernel
7
+
8
+ private
9
+
10
+ def xsel data = nil, sel: :primary
11
+ if ($xsel ||= command? "xsel" ) then
12
+ cmd = [ $xsel]
13
+ case sel
14
+ when :primary then cmd.push "-p"
15
+ when :secondary then cmd.push "-s"
16
+ when :clipboard then cmd.push "-b"
17
+ end
18
+ ci, co = "-i", "-o"
19
+ elsif ($xclip ||= command? "xclip") then
20
+ cmd = [ $xclip, "-selection", sel.to_s]
21
+ ci, co = "-i", "-o"
22
+ end
23
+ cmd or raise ScriptError, "Sorry, Neither xsel nor xclip seem to be installed."
24
+ if data then
25
+ cmd.push ci
26
+ cmd_write cmd, data
27
+ else
28
+ cmd.push co
29
+ cmd_read cmd
30
+ end
31
+ end
32
+ def xsel! data, sel: :clipboard
33
+ xsel data, sel: sel
34
+ end
35
+
36
+ def tmuxbuf data = nil, name: nil
37
+ $tmux ||= command? "tmux"
38
+ $tmux or raise ScriptError, "Sorry, TMux doesn't seem to be installed."
39
+ args = []
40
+ if name then
41
+ args.push "-b", name
42
+ end
43
+ args.push "-"
44
+ if data then
45
+ cmd_write [ $tmux, "load-buffer", *args], data
46
+ else
47
+ cmd_read [ $tmux, "save-buffer", *args]
48
+ end
49
+ end
50
+
51
+
52
+ def command? cmd
53
+ if cmd[ File::SEPARATOR] then
54
+ cmd if File.executable? cmd
55
+ else
56
+ (ENV[ "PATH"].split File::PATH_SEPARATOR).each { |p|
57
+ c = File.join p, cmd
58
+ return c if File.executable? c
59
+ }
60
+ nil
61
+ end
62
+ end
63
+
64
+ def cmd_write cmd, data
65
+ case data
66
+ when Array then data = data.join $/
67
+ when String then nil
68
+ else data = data.to_s
69
+ end
70
+ IO.popen cmd, "w" do |t| t.write data end
71
+ end
72
+
73
+ def cmd_read cmd
74
+ IO.popen cmd, "r" do |t| t.read end
75
+ end
76
+
77
+ end
78
+