nvim 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+