nrepl-lazuli 0.2.6 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb08e5429998d1320a2f3b4db07b336c389af0e3a1f008c67dbb4236af27af4a
4
- data.tar.gz: 9eb7a44c8d716860eeb09f18025dd5dd47713554384e9be249683b1ec7b11bf9
3
+ metadata.gz: 2439208ce1a3db5af7c886aa95b577bb713667461c92d129e997f378ca704926
4
+ data.tar.gz: 6aabf3d36d3996ff158a2c50ebaa6239570e4799614a8d7110d50df7ca1ede4f
5
5
  SHA512:
6
- metadata.gz: f3aec4c5b770994bb11be7f6fdfcbbaa4c0a1050e4964c0a10cd0cf4b95c6014ce0dd40cc765cad1815b93f429bb0c2a3a66a70aa03f730392d03bad0c71e29c
7
- data.tar.gz: 33d341eddae865f76f8463353b2232aaec4edd480f12301856bf618223f8f23fe7b237397e5d39135e3d745e9c57cbf22c6e658b61f9fa68c9756ccc81c65c35
6
+ metadata.gz: 755e29ac97e588baca03a48914cc968d23d96961c66c4a3778554bc29b2d14f05ac236f9d15a1b565318cab9a11c28bc1a07cf096911da656f1450c4ae52ac7f
7
+ data.tar.gz: a6fabeadef479c719f9fa60b53587e85d0d25fd88e96a03311103b0a5a581cccb54425ebd68c9e75cb01e69335dc755631699153bb44501ec51d756d0dc693a8
@@ -4,18 +4,18 @@ module NREPL
4
4
 
5
5
  def initialize(
6
6
  input, debug: false, out: input,
7
- watches: NREPL.class_variable_get(:@@bindings),
8
7
  binding: nil,
9
- bindings: {}
8
+ bindings: {},
9
+ bindings_by_id: {}
10
10
  )
11
11
  @debug = debug
12
12
  @in = input
13
13
  @out = out
14
14
  @pending_evals = {}
15
- @watches = watches
16
15
  @counter = 0
17
16
  @binding = binding
18
17
  @bindings = bindings
18
+ @bindings_by_id = bindings_by_id
19
19
  end
20
20
 
21
21
  def treat_messages!
@@ -28,6 +28,8 @@ module NREPL
28
28
  treat_msg(msg)
29
29
  end
30
30
  @pending_evals.each { |(i, _)| clear_eval!(i) }
31
+ rescue Errno::ECONNRESET
32
+ @pending_evals.each { |(i, _)| clear_eval!(i) }
31
33
  end
32
34
 
33
35
  def treat_msg(msg)
@@ -40,6 +42,9 @@ module NREPL
40
42
  eval_op(msg, false)
41
43
  when 'eval_pause'
42
44
  eval_op(msg, true)
45
+ when 'last_exception'
46
+ ex = Server.class_variable_get(:@@last_exception)
47
+ send_msg(response_for(msg, { 'result' => ex.inspect, 'status' => ['done'] }))
43
48
  when 'eval_resume'
44
49
  msg['id'] ||= "eval_#{@counter += 1}"
45
50
  stop_id = msg['stop_id']
@@ -52,7 +57,14 @@ module NREPL
52
57
  when 'unwatch'
53
58
  msg['id'] ||= "eval_#{@counter += 1}"
54
59
  watch_id = msg['watch_id']
55
- @watches.delete(watch_id)
60
+ info = @bindings_by_id.delete(watch_id)
61
+ if(info)
62
+ file_info = @bindings.fetch(info[:file], {})
63
+ content = file_info.fetch(info[:row], {})
64
+ content.delete(watch_id)
65
+ file_info.delete(info[:row]) if(content.empty?)
66
+ @bindings.delete(info[:file]) if(file_info.empty?)
67
+ end
56
68
 
57
69
  send_msg(response_for(msg, {
58
70
  'status' => ['done'],
@@ -110,7 +122,6 @@ module NREPL
110
122
  begin
111
123
  eval_msg(msg, stop)
112
124
  rescue Exception => e
113
- puts e.backtrace
114
125
  send_exception(msg, e)
115
126
  ensure
116
127
  @pending_evals.delete(id) unless stop
@@ -150,7 +161,7 @@ module NREPL
150
161
  original_bind = if msg['stop_id']
151
162
  @pending_evals.fetch(msg['stop_id'], {})[:binding]
152
163
  elsif msg['watch_id']
153
- @watches.fetch(msg['watch_id'], {})[:binding]
164
+ @bindings_by_id.fetch(msg['watch_id'], {})[:binding]
154
165
  else
155
166
  find_row_based_binding(msg) || @binding
156
167
  end
@@ -162,6 +173,12 @@ module NREPL
162
173
  end
163
174
  end
164
175
 
176
+ private def evaluate_code(code, file, line, bind)
177
+ bind ||= @@binding
178
+ line = line ? line + 1 : 1
179
+ eval(code, bind, file || "EVAL", line).inspect
180
+ end
181
+
165
182
  private def find_row_based_binding(msg)
166
183
  file = msg['file']
167
184
  row = msg['line']
@@ -170,7 +187,7 @@ module NREPL
170
187
  rows_bindings = @bindings[file]
171
188
  return unless rows_bindings
172
189
  found_row = row.downto(-1).find { |k| rows_bindings[k] }
173
- rows_bindings[found_row][:binding] if found_row
190
+ rows_bindings[found_row].values[-1] if found_row
174
191
  end
175
192
 
176
193
  private def define_stop_function!(msg, method_name)
@@ -228,9 +245,6 @@ module NREPL
228
245
  send_msg(response_for(msg, { 'versions' => versions }))
229
246
  end
230
247
 
231
- # @param [TCPSocket] client
232
- # @param [Hash] msg
233
- # @param [Exception] e
234
248
  def send_exception(msg, e)
235
249
  send_msg(response_for(msg, { 'ex' => e.message, 'status' => ['done', 'error'] }))
236
250
  end
@@ -244,8 +258,4 @@ module NREPL
244
258
  end
245
259
 
246
260
  # To avoid locally binding with the NREPL::Connection module
247
- b = binding
248
- define_method(:evaluate_code) do |code, file, line, bind|
249
- bind ||= b
250
- eval(code, bind, file || "EVAL", line || 0).inspect
251
- end
261
+ NREPL::Connection.class_variable_set(:@@binding, binding)
@@ -13,6 +13,14 @@ module NREPL
13
13
  attr_reader :debug, :port, :host
14
14
  alias debug? debug
15
15
 
16
+ def self.spawn(args = {})
17
+ t = Thread.new {
18
+ new(**args).start
19
+ }
20
+ sleep 0.1
21
+ t[:nrepl_server]
22
+ end
23
+
16
24
  def self.start(**kwargs)
17
25
  new(**kwargs).start
18
26
  end
@@ -21,7 +29,14 @@ module NREPL
21
29
  new(**kwargs.merge(binding: binding)).start
22
30
  end
23
31
 
24
- def initialize(port: DEFAULT_PORT, host: DEFAULT_HOST, debug: false, binding: nil, pwd: Dir.pwd)
32
+ def initialize(
33
+ port: DEFAULT_PORT,
34
+ host: DEFAULT_HOST,
35
+ debug: false,
36
+ binding: nil,
37
+ pwd: Dir.pwd,
38
+ tracing: true
39
+ )
25
40
  @port = port
26
41
  @pwd = pwd
27
42
  @host = host
@@ -29,8 +44,12 @@ module NREPL
29
44
  @connections = Set.new
30
45
  @binding = binding
31
46
  @bindings = {}
47
+ @bindings_by_id = {}
48
+ @tracing = tracing
49
+ Thread.current[:nrepl_server] = self
32
50
  NREPL.class_variable_set(:@@connections, @connections)
33
51
  NREPL.class_variable_set(:@@bindings, @bindings)
52
+ NREPL.class_variable_set(:@@bindings_by_id, @bindings_by_id)
34
53
  end
35
54
 
36
55
  private def record_port
@@ -44,31 +63,45 @@ module NREPL
44
63
  puts "Running in debug mode" if debug?
45
64
  record_port
46
65
 
66
+ @old_out, @old_err = $stdout, $stderr
47
67
  $stdout = FakeStdout.new(@connections, STDOUT, "out")
48
68
  $stderr = FakeStdout.new(@connections, STDERR, "err")
49
- auto_create_bindings!
69
+ auto_create_bindings! if @tracing
50
70
 
51
71
  Signal.trap("INT") { stop }
52
72
  Signal.trap("TERM") { stop }
53
73
 
54
- s = TCPServer.new(host, port)
74
+ @socket = TCPServer.new(host, port)
55
75
  loop do
56
- Thread.start(s.accept) do |client|
57
- connection = Connection.new(client, debug: debug?, binding: @binding, bindings: @bindings)
76
+ break if @socket.closed?
77
+ Thread.start(@socket.accept) do |client|
78
+ connection = Connection.new(
79
+ client,
80
+ debug: debug?,
81
+ binding: @binding,
82
+ bindings_by_id: @bindings_by_id,
83
+ bindings: @bindings
84
+ )
58
85
  @connections << connection
59
86
  connection.treat_messages!
60
87
  @connections.delete(connection)
61
88
  end
62
89
  end
90
+ rescue IOError
91
+ puts "Server closed" if debug?
63
92
  ensure
64
93
  File.unlink(PORT_FILENAME)
65
94
  end
66
95
 
67
96
  def auto_create_bindings!
68
97
  dir_regex = Regexp.new("^#{Regexp.escape(@pwd)}")
69
- @call_trace = TracePoint.new(:call) do |tp|
98
+ @call_trace = TracePoint.new(:class, :call) do |tp|
99
+ id = "#{tp.path}:#{tp.lineno}"
100
+ b = tp.binding
101
+ @bindings_by_id[id] = {binding: b, file: tp.path, row: tp.lineno-1}
70
102
  @bindings[tp.path] ||= {}
71
- @bindings[tp.path][tp.lineno-1] = {binding: tp.binding, id: "#{tp.path}:#{tp.lineno}"}
103
+ @bindings[tp.path][tp.lineno-1] ||= {}
104
+ @bindings[tp.path][tp.lineno-1][id] = b
72
105
  if tp.path =~ dir_regex
73
106
  @connections.each do |connection|
74
107
  connection.send_msg(
@@ -82,16 +115,17 @@ module NREPL
82
115
  end
83
116
  @call_trace.enable
84
117
 
85
- # @ex_trace = TracePoint.new(:raise) do |tp|
86
- # $foo = [tp.lineno, tp.event, tp.raised_exception, tp.binding, caller_locations]
87
- # # p $foo
88
- # end
89
- # @ex_trace.enable
118
+ @ex_trace = TracePoint.new(:raise) do |tp|
119
+ e = tp.raised_exception
120
+ @@last_exception = [:error, e.inspect.sub(/\>$/, ' stack=' + e.backtrace.inspect+'>')]
121
+ end
122
+ @ex_trace.enable
90
123
  end
91
124
 
92
125
  def stop
93
- Thread.exit
94
- exit(0)
126
+ @connections = []
127
+ $stdout, $stderr = @old_out, @old_err
128
+ @socket.close
95
129
  end
96
130
  end
97
131
 
data/lib/nrepl-lazuli.rb CHANGED
@@ -8,16 +8,19 @@ module NREPL
8
8
 
9
9
  require_relative 'nrepl-lazuli/server'
10
10
  @@bindings = {}
11
+ @@bindings_by_id = {}
11
12
  @@connections = Set.new
12
13
 
13
14
  def self.watch!(binding, id=nil)
14
15
  loc = caller_locations[0]
15
- file = loc.absolute_path
16
+ file = loc.path
16
17
  row = loc.lineno
17
18
  id ||= "#{file}:#{row}"
18
19
 
20
+ @@bindings_by_id[id] = {binding: binding, file: file, row: loc.lineno-1}
19
21
  @@bindings[file] ||= {}
20
- @@bindings[file][loc.lineno-1] = {id: id, binding: binding}
22
+ @@bindings[file][loc.lineno-1] ||= {}
23
+ @@bindings[file][loc.lineno-1][id] = binding
21
24
 
22
25
  @@connections.each do |connection|
23
26
  connection.send_msg(
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nrepl-lazuli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maurício Szabo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-05 00:00:00.000000000 Z
11
+ date: 2024-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bencode
@@ -54,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  requirements: []
57
- rubygems_version: 3.5.3
57
+ rubygems_version: 3.5.9
58
58
  signing_key:
59
59
  specification_version: 4
60
60
  summary: A Ruby nREPL server