nrepl-lazuli 0.1.2 → 0.2.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: e9806d1a707607c97f52c191fe34e6027fd58740ada2692d5f998e5d81e9e2e2
4
- data.tar.gz: fbaa80613144afaa7cd5c0d56db8d0655819008895fc10a59904a1980dae033c
3
+ metadata.gz: 2deef62506da60f639206583fc37504630c3fdefcab9013a1ef25b354f51221c
4
+ data.tar.gz: 8374c37e1eae4d69479137163f10242ea0f287c9ffd099830b1d3d361b6c344c
5
5
  SHA512:
6
- metadata.gz: fa9bbcfed7f0fb97e91805297589ab1341553d82372caf318e1118332bf1e74e4e39898cce008af093f10e2678f75505d82e4502c8b42f887ef6d75823f0cf36
7
- data.tar.gz: 1fc41134491cd81daa30bf1148c9c1ce09644922d4342e4745cc48b56ab009be68784874d0e6ef34d946ae43dd90b237d7910bf60c4198fb125860987d196cc5
6
+ metadata.gz: c81e136417a9d6dac29f007a2539011baebc83d6975f1715eaa3f6f0799b4e63d4484bf834e729c0b53f65b0dcff09c917443f41c1f50edb1104cf3efb740390
7
+ data.tar.gz: a6293282dc353c2cfa998fd557a1c84a7482224009ea905f08f82c3615e38e64b4507f5927630be9f6e4e7a2d6a00a05d02e735f59b357249ccab0d7840e4a6c
@@ -2,13 +2,19 @@ module NREPL
2
2
  class Connection
3
3
  @@debug_counter = 0
4
4
 
5
- def initialize(input, debug: false, out: input, watches: NREPL.class_variable_get(:@@watches))
5
+ def initialize(
6
+ input, debug: false, out: input,
7
+ watches: NREPL.class_variable_get(:@@watches), binding: nil,
8
+ auto_bindings: {}
9
+ )
6
10
  @debug = debug
7
11
  @in = input
8
12
  @out = out
9
13
  @pending_evals = {}
10
14
  @watches = watches
11
15
  @counter = 0
16
+ @binding = binding
17
+ @auto_bindings = auto_bindings
12
18
  end
13
19
 
14
20
  def treat_messages!
@@ -75,7 +81,15 @@ module NREPL
75
81
  'op' => msg['op']
76
82
  }))
77
83
  end
78
-
84
+ when 'watches_for_file'
85
+ msg['id'] ||= "eval_#{@counter += 1}"
86
+ file = msg['file']
87
+ rows_bindings = @auto_bindings[file] || {}
88
+ send_msg(response_for(msg, {
89
+ 'status' => ['done'],
90
+ 'rows' => rows_bindings.keys.sort,
91
+ 'op' => msg['op']
92
+ }))
79
93
  else
80
94
  send_msg(response_for(msg, {
81
95
  'op' => msg['op'],
@@ -135,6 +149,8 @@ module NREPL
135
149
  @pending_evals.fetch(msg['stop_id'], {})[:binding]
136
150
  elsif msg['watch_id']
137
151
  @watches.fetch(msg['watch_id'], {})[:binding]
152
+ else
153
+ find_row_based_binding(msg) || @binding
138
154
  end
139
155
  evaluate_code(code, msg['file'], msg['line'], original_bind)
140
156
  end
@@ -144,6 +160,17 @@ module NREPL
144
160
  end
145
161
  end
146
162
 
163
+ private def find_row_based_binding(msg)
164
+ file = msg['file']
165
+ row = msg['line']
166
+ return if !file || !row
167
+
168
+ rows_bindings = @auto_bindings[file]
169
+ return unless rows_bindings
170
+ found_row = row.downto(-1).find { |k| rows_bindings[k] }
171
+ rows_bindings[found_row]
172
+ end
173
+
147
174
  private def define_stop_function!(msg, method_name)
148
175
  out, inp = IO.pipe
149
176
  send_stopped = proc do |ctx_binding, original_msg, caller|
@@ -1,9 +1,12 @@
1
+ require "delegate"
2
+
1
3
  module NREPL
2
- class FakeStdout
4
+ class FakeStdout < SimpleDelegator
3
5
  def initialize(connections, io, kind)
4
6
  @connections = connections
5
7
  @io = io
6
8
  @kind = kind
9
+ super(io)
7
10
  end
8
11
 
9
12
  def <<(text)
@@ -21,33 +24,13 @@ module NREPL
21
24
  nil
22
25
  end
23
26
 
24
- def method_missing(method, *)
25
- @io.send(method, *)
26
- end
27
-
28
27
  def write(text)
29
- @io.write(text)
30
28
  @connections.each do |conn|
31
29
  conn.send_msg(
32
30
  @kind => text
33
31
  )
34
32
  end
35
- end
36
-
37
- def flush
38
- @io.flush
39
- end
40
-
41
- def close
42
- @io.close
43
- end
44
-
45
- def sync
46
- @io.sync
47
- end
48
-
49
- def sync=(val)
50
- @io.sync = val
33
+ @io.write(text)
51
34
  end
52
35
  end
53
36
  end
data/lib/nrepl/server.rb CHANGED
@@ -17,11 +17,18 @@ module NREPL
17
17
  new(**kwargs).start
18
18
  end
19
19
 
20
- def initialize(port: DEFAULT_PORT, host: DEFAULT_HOST, debug: false)
20
+ def self.bind!(binding, **kwargs)
21
+ new(**kwargs.merge(binding: binding)).start
22
+ end
23
+
24
+ def initialize(port: DEFAULT_PORT, host: DEFAULT_HOST, debug: false, binding: nil, pwd: Dir.pwd)
21
25
  @port = port
26
+ @pwd = pwd
22
27
  @host = host
23
28
  @debug = debug
24
29
  @connections = Set.new
30
+ @binding = binding
31
+ @auto_bindings = {}
25
32
  NREPL.class_variable_set(:@@connections, @connections)
26
33
  end
27
34
 
@@ -38,6 +45,7 @@ module NREPL
38
45
 
39
46
  $stdout = FakeStdout.new(@connections, STDOUT, "out")
40
47
  $stderr = FakeStdout.new(@connections, STDERR, "err")
48
+ auto_create_bindings!
41
49
 
42
50
  Signal.trap("INT") { stop }
43
51
  Signal.trap("TERM") { stop }
@@ -45,7 +53,7 @@ module NREPL
45
53
  s = TCPServer.new(host, port)
46
54
  loop do
47
55
  Thread.start(s.accept) do |client|
48
- connection = Connection.new(client, debug: debug?)
56
+ connection = Connection.new(client, debug: debug?, binding: @binding, auto_bindings: @auto_bindings)
49
57
  @connections << connection
50
58
  connection.treat_messages!
51
59
  @connections.delete(connection)
@@ -55,23 +63,80 @@ module NREPL
55
63
  File.unlink(PORT_FILENAME)
56
64
  end
57
65
 
66
+ def auto_create_bindings!
67
+ 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
71
+ if tp.path =~ dir_regex
72
+ # puts "Tracing #{tp.path}:#{tp.lineno}"
73
+ @connections.each do |connection|
74
+ connection.send_msg(
75
+ 'op' => 'hit_auto_watch',
76
+ 'file' => tp.path,
77
+ 'line' => tp.lineno-1,
78
+ 'status' => ['done']
79
+ )
80
+ end
81
+ end
82
+ end
83
+ @call_trace.enable
84
+ end
85
+
58
86
  def stop
59
87
  Thread.exit
60
88
  exit(0)
61
89
  end
62
90
  end
63
- end
64
91
 
65
- # Sorry, no other way...
66
- module ThreadPatch
67
- def initialize(*args, &b)
68
- @parent = Thread.current
69
- super
92
+ # Sorry, no other way...
93
+ module ThreadPatch
94
+ def initialize(*args, &b)
95
+ @parent = Thread.current
96
+ super
97
+ end
98
+
99
+ def parent
100
+ @parent
101
+ end
102
+ end
103
+
104
+ Thread.prepend(ThreadPatch)
105
+
106
+ # Also...
107
+ module MethodLocationFixer
108
+ def __lazuli_source_location
109
+ @__lazuli_source_location || source_location
110
+ end
70
111
  end
71
112
 
72
- def parent
73
- @parent
113
+ module DefinitionFixer
114
+ @@definitions = {}
115
+
116
+ def __lazuli_source_location(method)
117
+ ancestors.each do |klass|
118
+ loc = (klass.instance_variable_get(:@__lazuli_methods) || {})[method]
119
+ return loc if loc
120
+ end
121
+ return instance_method(method).source_location
122
+ end
123
+
124
+ def method_added(method_name)
125
+ return if method_name == :__lazuli_source_location
126
+ # puts "Thing added #{method_name}"
127
+ path = caller.reject { |x| x =~ /gems.*gems/ }[0]
128
+ if path
129
+ (file, row) = path.split(/:/)
130
+
131
+ known = instance_variable_get(:@__lazuli_methods)
132
+ if !known
133
+ known = {}
134
+ instance_variable_set(:@__lazuli_methods, known)
135
+ end
136
+ known[method_name] = [file, row.to_i]
137
+ end
138
+ end
139
+
140
+ Module.prepend(DefinitionFixer)
74
141
  end
75
142
  end
76
-
77
- Thread.prepend(ThreadPatch)
data/lib/nrepl.rb CHANGED
@@ -25,5 +25,6 @@ module NREPL
25
25
  'status' => ['done']
26
26
  )
27
27
  end
28
+ nil
28
29
  end
29
30
  end
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.1.2
4
+ version: 0.2.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-03-22 00:00:00.000000000 Z
11
+ date: 2024-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bencode