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