nrepl-lazuli 0.2.5 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e198f7aeaa4e73c02bb95aab3cc78233e64730eb9da1c7a7a3b81100dc2063b1
4
- data.tar.gz: f5bb2f932d3520450821ec746527c52c9a2aeb7bf2946cf37f7aa98d1b92f10e
3
+ metadata.gz: 35d0ce415821f889b64d53b1ea8f88b11a96d43a8932e00108d991b3bfc7a7fb
4
+ data.tar.gz: 6b72192793c3bc68883c1396c4c86d1a1dbed7976692f75e1dfc1f3a0fecf938
5
5
  SHA512:
6
- metadata.gz: 8b4b0f03d9673e708b6aabad549544d812bd3d06e63748507e39595e748a02c5a2344ac5f1d6819cb501b462b667756c3225da6a6a1254b72488f0e1115f20ea
7
- data.tar.gz: 074bfe1128eac938c707fc33f2add4e388ab762ab0ecbd3113300829ffee2f67fe60f2246bf590953734065c68a40e8e398bd3a77b8104f7a81ce64a3f8bdf0a
6
+ metadata.gz: 5e6639603de1e57633eb29742732ffedb483d6c101cc96c2cb9c1d3a2940e2d2aa0a49c5510d8ac3eaceccc1ed69bea56dd0e20693813ef0714e9d5ecc3f1cad
7
+ data.tar.gz: bc667ae67a50840cab449027e7b6d1e990303e2986809823effec71d278811399ddc53aee3a561c3fc4dd3c0fa75ca81bcc40401b0301b0ce49f1a97f9de9f4d
@@ -4,17 +4,18 @@ module NREPL
4
4
 
5
5
  def initialize(
6
6
  input, debug: false, out: input,
7
- watches: NREPL.class_variable_get(:@@watches), binding: nil,
8
- auto_bindings: {}
7
+ binding: nil,
8
+ bindings: {},
9
+ bindings_by_id: {}
9
10
  )
10
11
  @debug = debug
11
12
  @in = input
12
13
  @out = out
13
14
  @pending_evals = {}
14
- @watches = watches
15
15
  @counter = 0
16
16
  @binding = binding
17
- @auto_bindings = auto_bindings
17
+ @bindings = bindings
18
+ @bindings_by_id = bindings_by_id
18
19
  end
19
20
 
20
21
  def treat_messages!
@@ -27,6 +28,8 @@ module NREPL
27
28
  treat_msg(msg)
28
29
  end
29
30
  @pending_evals.each { |(i, _)| clear_eval!(i) }
31
+ rescue Errno::ECONNRESET
32
+ @pending_evals.each { |(i, _)| clear_eval!(i) }
30
33
  end
31
34
 
32
35
  def treat_msg(msg)
@@ -39,6 +42,9 @@ module NREPL
39
42
  eval_op(msg, false)
40
43
  when 'eval_pause'
41
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'] }))
42
48
  when 'eval_resume'
43
49
  msg['id'] ||= "eval_#{@counter += 1}"
44
50
  stop_id = msg['stop_id']
@@ -51,7 +57,14 @@ module NREPL
51
57
  when 'unwatch'
52
58
  msg['id'] ||= "eval_#{@counter += 1}"
53
59
  watch_id = msg['watch_id']
54
- @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
55
68
 
56
69
  send_msg(response_for(msg, {
57
70
  'status' => ['done'],
@@ -84,7 +97,7 @@ module NREPL
84
97
  when 'watches_for_file'
85
98
  msg['id'] ||= "eval_#{@counter += 1}"
86
99
  file = msg['file']
87
- rows_bindings = @auto_bindings[file] || {}
100
+ rows_bindings = @bindings[file] || {}
88
101
  send_msg(response_for(msg, {
89
102
  'status' => ['done'],
90
103
  'rows' => rows_bindings.keys.sort,
@@ -148,7 +161,7 @@ module NREPL
148
161
  original_bind = if msg['stop_id']
149
162
  @pending_evals.fetch(msg['stop_id'], {})[:binding]
150
163
  elsif msg['watch_id']
151
- @watches.fetch(msg['watch_id'], {})[:binding]
164
+ @bindings_by_id.fetch(msg['watch_id'], {})[:binding]
152
165
  else
153
166
  find_row_based_binding(msg) || @binding
154
167
  end
@@ -165,10 +178,10 @@ module NREPL
165
178
  row = msg['line']
166
179
  return if !file || !row
167
180
 
168
- rows_bindings = @auto_bindings[file]
181
+ rows_bindings = @bindings[file]
169
182
  return unless rows_bindings
170
183
  found_row = row.downto(-1).find { |k| rows_bindings[k] }
171
- rows_bindings[found_row]
184
+ rows_bindings[found_row].values[-1] if found_row
172
185
  end
173
186
 
174
187
  private def define_stop_function!(msg, method_name)
@@ -226,9 +239,6 @@ module NREPL
226
239
  send_msg(response_for(msg, { 'versions' => versions }))
227
240
  end
228
241
 
229
- # @param [TCPSocket] client
230
- # @param [Hash] msg
231
- # @param [Exception] e
232
242
  def send_exception(msg, e)
233
243
  send_msg(response_for(msg, { 'ex' => e.message, 'status' => ['done', 'error'] }))
234
244
  end
@@ -245,5 +255,6 @@ end
245
255
  b = binding
246
256
  define_method(:evaluate_code) do |code, file, line, bind|
247
257
  bind ||= b
248
- eval(code, bind, file || "EVAL", line || 0).inspect
258
+ line = line ? line + 1 : 1
259
+ eval(code, bind, file || "EVAL", line).inspect
249
260
  end
@@ -24,7 +24,8 @@ module NREPL
24
24
  nil
25
25
  end
26
26
 
27
- def write(text='')
27
+ def write(*texts)
28
+ text = texts.join('')
28
29
  @connections.each do |conn|
29
30
  conn.send_msg(
30
31
  @kind => text
@@ -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,15 +29,27 @@ 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
28
43
  @debug = debug
29
44
  @connections = Set.new
30
45
  @binding = binding
31
- @auto_bindings = {}
46
+ @bindings = {}
47
+ @bindings_by_id = {}
48
+ @tracing = tracing
49
+ Thread.current[:nrepl_server] = self
32
50
  NREPL.class_variable_set(:@@connections, @connections)
51
+ NREPL.class_variable_set(:@@bindings, @bindings)
52
+ NREPL.class_variable_set(:@@bindings_by_id, @bindings_by_id)
33
53
  end
34
54
 
35
55
  private def record_port
@@ -43,33 +63,46 @@ module NREPL
43
63
  puts "Running in debug mode" if debug?
44
64
  record_port
45
65
 
66
+ @old_out, @old_err = $stdout, $stderr
46
67
  $stdout = FakeStdout.new(@connections, STDOUT, "out")
47
68
  $stderr = FakeStdout.new(@connections, STDERR, "err")
48
- auto_create_bindings!
69
+ auto_create_bindings! if @tracing
49
70
 
50
71
  Signal.trap("INT") { stop }
51
72
  Signal.trap("TERM") { stop }
52
73
 
53
- s = TCPServer.new(host, port)
74
+ @socket = TCPServer.new(host, port)
54
75
  loop do
55
- Thread.start(s.accept) do |client|
56
- connection = Connection.new(client, debug: debug?, binding: @binding, auto_bindings: @auto_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
+ )
57
85
  @connections << connection
58
86
  connection.treat_messages!
59
87
  @connections.delete(connection)
60
88
  end
61
89
  end
90
+ rescue IOError
91
+ puts "Server closed" if debug?
62
92
  ensure
63
93
  File.unlink(PORT_FILENAME)
64
94
  end
65
95
 
66
96
  def auto_create_bindings!
67
97
  dir_regex = Regexp.new("^#{Regexp.escape(@pwd)}")
68
- @call_trace = TracePoint.new(:call) do |tp|
69
- @auto_bindings[tp.path] ||= {}
70
- @auto_bindings[tp.path][tp.lineno-1] = tp.binding
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}
102
+ @bindings[tp.path] ||= {}
103
+ @bindings[tp.path][tp.lineno-1] ||= {}
104
+ @bindings[tp.path][tp.lineno-1][id] = b
71
105
  if tp.path =~ dir_regex
72
- # puts "Tracing #{tp.path}:#{tp.lineno}"
73
106
  @connections.each do |connection|
74
107
  connection.send_msg(
75
108
  'op' => 'hit_auto_watch',
@@ -81,11 +114,18 @@ module NREPL
81
114
  end
82
115
  end
83
116
  @call_trace.enable
117
+
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
84
123
  end
85
124
 
86
125
  def stop
87
- Thread.exit
88
- exit(0)
126
+ @connections = []
127
+ $stdout, $stderr = @old_out, @old_err
128
+ @socket.close
89
129
  end
90
130
  end
91
131
 
data/lib/nrepl-lazuli.rb CHANGED
@@ -7,15 +7,21 @@ module NREPL
7
7
  PORT_FILENAME = '.nrepl-port'
8
8
 
9
9
  require_relative 'nrepl-lazuli/server'
10
- @@watches = {}
10
+ @@bindings = {}
11
+ @@bindings_by_id = {}
11
12
  @@connections = Set.new
12
13
 
13
14
  def self.watch!(binding, id=nil)
14
- (file, row) = caller[0].split(/:/)
15
+ loc = caller_locations[0]
16
+ file = loc.path
17
+ row = loc.lineno
15
18
  id ||= "#{file}:#{row}"
16
- row = row.to_i
17
19
 
18
- @@watches[id] = {binding: binding}
20
+ @@bindings_by_id[id] = {binding: binding, file: file, row: loc.lineno-1}
21
+ @@bindings[file] ||= {}
22
+ @@bindings[file][loc.lineno-1] ||= {}
23
+ @@bindings[file][loc.lineno-1][id] = binding
24
+
19
25
  @@connections.each do |connection|
20
26
  connection.send_msg(
21
27
  'op' => 'hit_watch',
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.5
4
+ version: 0.3.0
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-04-20 00:00:00.000000000 Z
11
+ date: 2024-06-13 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