nvim 1.0.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.
- checksums.yaml +7 -0
- data/INFO.yaml +18 -0
- data/LICENSE +52 -0
- data/README.md +264 -0
- data/Rakefile +68 -0
- data/bin/neovim-ruby-host +51 -0
- data/lib/neovim/client.rb +108 -0
- data/lib/neovim/connection.rb +129 -0
- data/lib/neovim/foreign/mplight/bufferio.rb +51 -0
- data/lib/neovim/foreign/mplight.rb +327 -0
- data/lib/neovim/foreign/supplement/socket.rb +20 -0
- data/lib/neovim/foreign/supplement.rb +34 -0
- data/lib/neovim/handler.rb +139 -0
- data/lib/neovim/host.rb +87 -0
- data/lib/neovim/info.rb +10 -0
- data/lib/neovim/logging.rb +249 -0
- data/lib/neovim/messager.rb +185 -0
- data/lib/neovim/meta.rb +68 -0
- data/lib/neovim/remote.rb +56 -0
- data/lib/neovim/remote_object.rb +331 -0
- data/lib/neovim/ruby_provider.rb +372 -0
- data/lib/neovim/session.rb +60 -0
- data/lib/neovim/vimscript_provider.rb +49 -0
- data/lib/neovim.rb +22 -0
- metadata +66 -0
@@ -0,0 +1,249 @@
|
|
1
|
+
#
|
2
|
+
# neovim/logging.rb -- Logging facility
|
3
|
+
#
|
4
|
+
|
5
|
+
require "neovim/foreign/supplement"
|
6
|
+
|
7
|
+
|
8
|
+
module Neovim
|
9
|
+
|
10
|
+
module Logging
|
11
|
+
|
12
|
+
class Logger
|
13
|
+
|
14
|
+
class <<self
|
15
|
+
|
16
|
+
private :new
|
17
|
+
|
18
|
+
SUBS = []
|
19
|
+
|
20
|
+
def inherited cls
|
21
|
+
SUBS.push cls
|
22
|
+
end
|
23
|
+
|
24
|
+
def provide str
|
25
|
+
opened = nil
|
26
|
+
args = str.to_s.split ":"
|
27
|
+
args.each { |a| a.gsub! %r/%(\h\h)/ do ($1.to_i 0x10).chr end }
|
28
|
+
cls =
|
29
|
+
if args.first =~ /\A\w+\z/ then
|
30
|
+
prot = args.shift
|
31
|
+
(SUBS.find { |s| s::NAME == prot rescue nil }) or raise "Logger not found: #{str}"
|
32
|
+
else
|
33
|
+
Text
|
34
|
+
end
|
35
|
+
dest = args.shift
|
36
|
+
cls.open dest, **(parse_arguments args) do |i|
|
37
|
+
opened = true
|
38
|
+
yield i
|
39
|
+
end
|
40
|
+
rescue # Errno::EACCES, Errno::ENOENT
|
41
|
+
raise unless not opened and str.notempty?
|
42
|
+
$stderr.puts "Failed to open log file '#{str}'. Logging to stderr."
|
43
|
+
str = nil
|
44
|
+
retry
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def parse_arguments args
|
50
|
+
r = {}
|
51
|
+
args.each { |a|
|
52
|
+
k, v = a.split "=", 2
|
53
|
+
r[ k.to_sym] = parse_value v
|
54
|
+
}
|
55
|
+
r
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_value val
|
59
|
+
if val.nil? then
|
60
|
+
true
|
61
|
+
elsif val =~ /\A(?:0x)?\d+\z/ then
|
62
|
+
Integer val
|
63
|
+
else
|
64
|
+
case val.downcase
|
65
|
+
when "true", "yes", "on" then true
|
66
|
+
when "false", "no", "off" then false
|
67
|
+
else val.notempty?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
class Null < Logger
|
77
|
+
NAME = "null"
|
78
|
+
class <<self
|
79
|
+
def open dest = nil
|
80
|
+
yield new
|
81
|
+
end
|
82
|
+
end
|
83
|
+
def put **fields
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Stream < Logger
|
88
|
+
|
89
|
+
class <<self
|
90
|
+
def open path = nil, **kwargs
|
91
|
+
if path.notempty? and path != "-" then
|
92
|
+
params = {}
|
93
|
+
%i(external_encoding newline).each do |k|
|
94
|
+
v = kwargs.delete k
|
95
|
+
params[ k] = v if v
|
96
|
+
end
|
97
|
+
File.open path, "a", **params do |f|
|
98
|
+
yield (new f, **kwargs)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
yield (new $stderr, **kwargs)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def initialize file, **kwargs
|
107
|
+
@file = file
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
class Text < Stream
|
113
|
+
NAME = "file"
|
114
|
+
def initialize file, color: nil, short: nil
|
115
|
+
super
|
116
|
+
@color =
|
117
|
+
case color
|
118
|
+
when true, false then color
|
119
|
+
when 0 then false
|
120
|
+
when Integer then true
|
121
|
+
else @file.tty?
|
122
|
+
end
|
123
|
+
@short = short
|
124
|
+
end
|
125
|
+
COLORS = %w(33 32 34;1 4 31;1 35;1 36)
|
126
|
+
def put **fields
|
127
|
+
put_sep
|
128
|
+
l = [
|
129
|
+
((fields.delete :time).strftime "%H:%M:%S"),
|
130
|
+
((fields.delete :pid).to_s.rjust 5),
|
131
|
+
(fields.delete :caller).to_s[ %r([^/]+:\d+)],
|
132
|
+
(fields.delete :level),
|
133
|
+
(fields.delete :message).inspect,
|
134
|
+
((fields.delete :class).to_s.sub /.*::/, ""),
|
135
|
+
((fields.map { |k,v| "#{k}:#{v}" }.join " ").axe 256),
|
136
|
+
]
|
137
|
+
if @color then
|
138
|
+
l = l.zip COLORS
|
139
|
+
l.map! do |f,c| "\e[#{c}m#{f}\e[m" end
|
140
|
+
end
|
141
|
+
if @short then
|
142
|
+
s = l.shift 3
|
143
|
+
if not @nexttime or @nexttime < Time.now then
|
144
|
+
@file.puts s.join " "
|
145
|
+
@nexttime = Time.now + 120
|
146
|
+
end
|
147
|
+
end
|
148
|
+
@file.puts l.join " "
|
149
|
+
@file.flush
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
def put_sep
|
153
|
+
if @file.tty? then
|
154
|
+
if not @nextsep or @nextsep < Time.now then
|
155
|
+
@file.puts $/*5
|
156
|
+
@nextsep = Time.now + 300
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class Plain < Stream
|
163
|
+
NAME = "plain"
|
164
|
+
def put **fields
|
165
|
+
@file.puts fields.to_s
|
166
|
+
@file.flush
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
class Json < Stream
|
171
|
+
NAME = "json"
|
172
|
+
def initialize file
|
173
|
+
super
|
174
|
+
require "json"
|
175
|
+
require "time"
|
176
|
+
end
|
177
|
+
def put **fields
|
178
|
+
fields[ :time] = fields[ :time].iso8601 rescue nil
|
179
|
+
@file.puts fields.to_json
|
180
|
+
@file.flush
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
LEVELS = {}
|
186
|
+
%i(panic fatal error warn info debug1 debug2 debug3).each_with_index { |l,i| LEVELS[ l] = i }
|
187
|
+
LEVELS.default = LEVELS.length
|
188
|
+
DEFAULT_LEVEL = :warn
|
189
|
+
|
190
|
+
class <<self
|
191
|
+
|
192
|
+
attr_reader :level
|
193
|
+
attr_accessor :channel
|
194
|
+
|
195
|
+
def level= l
|
196
|
+
@level = l.to_sym.downcase
|
197
|
+
rescue NoMethodError
|
198
|
+
l = l.to_s
|
199
|
+
retry
|
200
|
+
end
|
201
|
+
|
202
|
+
def put level, message, **kwargs
|
203
|
+
return unless @channel
|
204
|
+
return if LEVELS[ level] > LEVELS[ @level]
|
205
|
+
@channel.put time: Time.now, pid: $$, level: level, message: message, **kwargs
|
206
|
+
nil
|
207
|
+
rescue
|
208
|
+
$stderr.puts "Failed to log: #$! (#{$!.class})"
|
209
|
+
$stderr.puts $@
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def log level, message, **kwargs
|
218
|
+
Logging.put level, message,
|
219
|
+
class: self.class, caller: (caller 1, 1).first,
|
220
|
+
**kwargs
|
221
|
+
end
|
222
|
+
|
223
|
+
def log_exception level
|
224
|
+
Logging.put level, "Exception: #$!",
|
225
|
+
class: self.class, caller: (caller 1, 1).first,
|
226
|
+
exception: $!.class
|
227
|
+
$@.each { |b|
|
228
|
+
Logging.put :debug3, "Backtrace", line: b
|
229
|
+
}
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
def open_logfile level: nil, path: nil
|
235
|
+
level ||= ENV[ "NVIM_RUBY_LOG_LEVEL"].notempty?
|
236
|
+
path ||= ENV[ "NVIM_RUBY_LOG_FILE" ].notempty?
|
237
|
+
Logger.provide path do |l|
|
238
|
+
ov, Logging.level = Logging.level, level||DEFAULT_LEVEL
|
239
|
+
ol, Logging.channel = Logging.channel, l
|
240
|
+
yield
|
241
|
+
ensure
|
242
|
+
Logging.level, Logging.channel = ov, ol
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
|
@@ -0,0 +1,185 @@
|
|
1
|
+
#
|
2
|
+
# neovim/messager.rb -- Send and Receive Messages
|
3
|
+
#
|
4
|
+
|
5
|
+
require "neovim/foreign/supplement"
|
6
|
+
|
7
|
+
require "neovim/logging"
|
8
|
+
|
9
|
+
|
10
|
+
module Neovim
|
11
|
+
|
12
|
+
class Messager
|
13
|
+
|
14
|
+
class Message
|
15
|
+
|
16
|
+
@subs, @subh = [], {}
|
17
|
+
|
18
|
+
class <<self
|
19
|
+
|
20
|
+
def from_array ary
|
21
|
+
kind, *payload = *ary
|
22
|
+
klass = find kind
|
23
|
+
klass[ *payload]
|
24
|
+
end
|
25
|
+
|
26
|
+
def inherited cls ; @subs.push cls ; end
|
27
|
+
def find id
|
28
|
+
@subh[ id] ||= @subs.find { |c| c::ID == id }
|
29
|
+
end
|
30
|
+
|
31
|
+
alias [] new
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize *args
|
36
|
+
z = self.class::KEYS.zip args
|
37
|
+
@cont = z.inject Hash.new do |c,(h,k)| c[h] = k ; c end
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
c = self.class.name.sub /.*::/, ""
|
42
|
+
"#<#{c} #@cont>"
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
c = self.class.name.sub /.*::/, ""
|
47
|
+
j = @cont.map { |k,v| "#{k}:#{v}" if v }.compact.join ","
|
48
|
+
"#{c}(#{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
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
class Notification < Message
|
90
|
+
ID = 2
|
91
|
+
KEYS = %i(method_name arguments)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
class ResponseError < StandardError ; end
|
97
|
+
|
98
|
+
class Disconnected < RuntimeError
|
99
|
+
def initialize
|
100
|
+
super "Lost connection to nvim process"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
include Logging
|
106
|
+
|
107
|
+
def initialize conn, session
|
108
|
+
@conn, @session = conn, session
|
109
|
+
@request_id = 0
|
110
|
+
@responses = {}
|
111
|
+
end
|
112
|
+
|
113
|
+
def run until_id = nil
|
114
|
+
loop do
|
115
|
+
message = get
|
116
|
+
case message
|
117
|
+
when Message::Response then
|
118
|
+
if @responses.key? message.request_id then
|
119
|
+
@responses[ message.request_id] = message
|
120
|
+
else
|
121
|
+
log :warning, "Dropped response", message.request_id
|
122
|
+
end
|
123
|
+
when Message::Request then
|
124
|
+
begin
|
125
|
+
r = @session.execute_handler message.method_name, message.arguments
|
126
|
+
log :debug1, "Request result", result: r
|
127
|
+
rescue
|
128
|
+
e = [ 0, $!.to_s]
|
129
|
+
log_exception :error
|
130
|
+
end
|
131
|
+
rsp = Message::Response.new message.request_id, e, r
|
132
|
+
put rsp
|
133
|
+
when Message::Notification then
|
134
|
+
begin
|
135
|
+
@session.execute_handler message.method_name, message.arguments
|
136
|
+
rescue
|
137
|
+
log_exception :error
|
138
|
+
end
|
139
|
+
end
|
140
|
+
break if until_id and @responses[ until_id]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def request method, *args
|
145
|
+
@request_id += 1
|
146
|
+
put Message::Request[ @request_id, method, args]
|
147
|
+
@responses[ @request_id] = nil
|
148
|
+
run @request_id
|
149
|
+
r = @responses.delete @request_id
|
150
|
+
if r.error then
|
151
|
+
t, e = *r.error
|
152
|
+
t = @conn.error t
|
153
|
+
raise ResponseError, "#{t}: #{e}"
|
154
|
+
end
|
155
|
+
r.value
|
156
|
+
end
|
157
|
+
|
158
|
+
def notify method, *args
|
159
|
+
put Message::Notification[ method, args]
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def put msg
|
165
|
+
log :debug1, "Sending Message", data: msg
|
166
|
+
@conn.put msg.to_a
|
167
|
+
self
|
168
|
+
rescue Errno::EPIPE
|
169
|
+
raise Disconnected
|
170
|
+
end
|
171
|
+
|
172
|
+
def get
|
173
|
+
IO.select [@conn.input], nil, nil
|
174
|
+
raise Disconnected if @conn.eof?
|
175
|
+
msg = Message.from_array @conn.get
|
176
|
+
log :debug1, "Received Message", data: msg
|
177
|
+
msg
|
178
|
+
rescue EOFError
|
179
|
+
raise Disconnected
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
data/lib/neovim/meta.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#
|
2
|
+
# neovim/meta.rb -- Metadata: Version info etc.
|
3
|
+
#
|
4
|
+
|
5
|
+
module Neovim
|
6
|
+
|
7
|
+
class Meta
|
8
|
+
|
9
|
+
def initialize name, **params
|
10
|
+
@name, @params = name, params
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :name
|
14
|
+
|
15
|
+
%i(
|
16
|
+
commit
|
17
|
+
version
|
18
|
+
license
|
19
|
+
summary
|
20
|
+
description
|
21
|
+
homepage
|
22
|
+
authors
|
23
|
+
email
|
24
|
+
metadata
|
25
|
+
).each { |p|
|
26
|
+
define_method p do @params[ p] end
|
27
|
+
}
|
28
|
+
|
29
|
+
alias website homepage
|
30
|
+
|
31
|
+
|
32
|
+
def version_h
|
33
|
+
@params[ :version] =~ /\Av?(\d+)(?:\.(\d+)(?:\.(\d+)))(?:-(.*))?\z/
|
34
|
+
{
|
35
|
+
major: $1,
|
36
|
+
minor: $2,
|
37
|
+
patch: $3,
|
38
|
+
prerelease: $4,
|
39
|
+
commit: @params[ :commit],
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def version_a
|
44
|
+
version_h.values_at :major, :minor, :patch, :prerelease
|
45
|
+
end
|
46
|
+
|
47
|
+
def attributes
|
48
|
+
{
|
49
|
+
website: @params[ :homepage],
|
50
|
+
license: @params[ :license ],
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def mk_gemspec spec
|
56
|
+
spec.name = @name
|
57
|
+
spec.version = @params[:version ]
|
58
|
+
spec.authors = @params[:authors ]
|
59
|
+
spec.email = @params[:email ]
|
60
|
+
spec.summary = @params[:summary ]
|
61
|
+
spec.homepage = @params[:homepage]
|
62
|
+
spec.license = @params[:license ]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#
|
2
|
+
# neovim/remote.rb -- Host for Neovim
|
3
|
+
#
|
4
|
+
|
5
|
+
require "neovim/session"
|
6
|
+
require "neovim/handler"
|
7
|
+
|
8
|
+
|
9
|
+
module Neovim
|
10
|
+
|
11
|
+
class Remote < Session
|
12
|
+
|
13
|
+
class <<self
|
14
|
+
|
15
|
+
def start_client *args
|
16
|
+
start *args do |i|
|
17
|
+
yield i.start
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize conn
|
24
|
+
super
|
25
|
+
@plugins = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def start
|
29
|
+
@conn.start @comm, client_name, self.class.name.downcase.to_sym, client_methods
|
30
|
+
@conn.client
|
31
|
+
end
|
32
|
+
|
33
|
+
def client_name ; "ruby-client" ; end
|
34
|
+
def client_methods ; end
|
35
|
+
|
36
|
+
|
37
|
+
def add_plugins source, plugins
|
38
|
+
@plugins[ source] = plugins
|
39
|
+
end
|
40
|
+
|
41
|
+
def execute_handler name, args
|
42
|
+
@plugins.each_value do |plugin|
|
43
|
+
handler = plugin.get_handler name
|
44
|
+
if handler then
|
45
|
+
log :info, "Found handler", name: name
|
46
|
+
log :debug1, "Calling with", args: args
|
47
|
+
return handler.execute @conn.client, *args
|
48
|
+
end
|
49
|
+
end
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|