nvim 1.0.0 → 1.1.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,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
+