nrepl-lazuli 0.2.5 → 0.3.0

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: 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