rider-server 0.1.0 → 0.1.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: 302928be71e4f0608534e86f87c555a0f3507004c93c058947b74d480cfde558
4
- data.tar.gz: 80707ddff979bb2a92f7f696dcbc44ac68830d56dffa11cfc021aa5eec06d743
3
+ metadata.gz: acb6833a29ab9facb82c72d8f2e012ab0dd7a44dbd1ad93dfe69f8d1bff2a765
4
+ data.tar.gz: 62834c0a615baa860e1e7650f14353180b874cf877833cc94b6f9804edd32efa
5
5
  SHA512:
6
- metadata.gz: f9fcc56b8cbceaa6df781155af166873e66c0bfe1261041b6667759dfc5eb56c3c9dfd6943b45e1e7f302cb0737e21da048ba399f837dec4a77416cb7af037bf
7
- data.tar.gz: 74527b4e988b97b6ed527530bbdef94cc6b48776c74fef12930dbf70a59c648209f3afa615ebb835861acba0b0eba70884107268b65e7aa12da0adb7b63040f8
6
+ metadata.gz: bec9b6906b7e47954bdf9a85fcb3a47ca89d86f63f68023b90d0c8ac83028f6aa7681ad0a4042934cd4a663a0f86f344d8ee11374816efe5616d5eaeaa2a8b75
7
+ data.tar.gz: a0d1b287bfeeaf68bbd82c9d72d7bb9405a9873286993e6171cd5f754c545c56cdcde1f186e8c9edb755a1e883a627ce6b33eeea2b21a047220ced97e3e973e5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.1] - 2024-07-31
4
+
5
+ - Basic completion
6
+ - Basic symbol lookup
7
+ - Fix ancestor lookup
8
+
3
9
  ## [0.1.0] - 2024-07-27
4
10
 
5
11
  - Initial release
data/COPYING ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2024 Russell Sim <rsl@simopolis.xyz>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ “Software”), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -32,10 +32,12 @@ dependencies. Then, run `rake test` to run the tests. You can also run
32
32
  experiment.
33
33
 
34
34
  To install this gem onto your local machine, run `bundle exec rake
35
- install`. To release a new version, update the version number in
36
- `version.rb`, and then run `bundle exec rake release`, which will
37
- create a git tag for the version, push git commits and the created
38
- tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
35
+ install`.
36
+
37
+ To release a new version run `rake bump:patch` and then run `bundle
38
+ exec rake release`, which will create a git tag for the version, push
39
+ git commits and the created tag, and push the `.gem` file to
40
+ [rubygems.org](https://rubygems.org).
39
41
 
40
42
  ## Contributing
41
43
 
@@ -11,14 +11,13 @@ require "rider_server/utils"
11
11
 
12
12
  module RiderServer
13
13
  module Inspect
14
-
15
14
  def self.safely_eval(string, file, line)
16
15
  code = <<~HEREDOC
17
- begin
18
- #{string}
19
- rescue StandardError => e
20
- e
21
- end
16
+ begin
17
+ #{string}
18
+ rescue StandardError => e
19
+ e
20
+ end
22
21
  HEREDOC
23
22
  eval(code, TOPLEVEL_BINDING, file, line) # rubocop:disable Security/Eval
24
23
  end
@@ -34,11 +33,8 @@ module RiderServer
34
33
  end
35
34
 
36
35
  def self.ancestors(obj)
37
- # Look for instance ancestors on the class.
38
- obj = obj.class if !obj.respond_to?(:ancestors) && obj.class.respond_to?(:ancestors)
39
-
40
- if obj.class.respond_to?(:ancestors)
41
- obj.class.ancestors.map do |item|
36
+ if obj.respond_to?(:ancestors)
37
+ obj.ancestors.map do |item|
42
38
  {
43
39
  "name" => item.to_s,
44
40
  "value" => Utils.rider_inspect(item),
@@ -104,14 +104,14 @@ module RiderServer
104
104
  response.set("out", e.full_message)
105
105
  response.status("eval-error", "done")
106
106
  if session
107
- exception_id = session.add_exception(operation["id"], e)
108
- response.set("rider/exception-id", exception_id)
107
+ exception = session.push_exception(operation["id"], e)
108
+ response.set("rider/exception-id", exception["id"])
109
109
  end
110
-
111
- # Broadcast the exception to sessions that are listening
112
- @sessions_catching_exceptions.each do |s|
113
- next if session == s
114
- @sessions[s]&.add_exception(operation["id"], e)
110
+ @sessions_catching_exceptions.each do |session_id|
111
+ session = @sessions[session_id]
112
+ next if session.nil?
113
+ exception = session.push_exception(operation["id"], e)
114
+ response.set("rider/exception-id", exception["id"])
115
115
  end
116
116
  send_response(response)
117
117
  end
@@ -9,6 +9,7 @@ module RiderServer
9
9
 
10
10
  argument :id, :string, "The request id", required: true
11
11
  argument :prefix, :string, "The prefix to complete", required: true
12
+ argument :complete_fn, :string, "What completion function to use, top-level, module, method or singleton-method"
12
13
  argument :ns, :string, "The namespace to search for completions"
13
14
 
14
15
  def initialize(*args)
@@ -20,38 +21,40 @@ module RiderServer
20
21
  response = Response.new(operation)
21
22
 
22
23
  prefix = operation["prefix"]
23
- log.info "prefix: #{prefix}"
24
24
  ns = operation["ns"]
25
- # options = operation["options"]
25
+ complete_fn = operation.fetch("complete-fn", "top-level")
26
26
 
27
- log.info "prefix: #{prefix}"
28
- ns_object = @workspace.lookup_module(ns)
29
- log.info "ns_object: #{ns_object}"
30
-
31
- response.set("completions", lookup_module(prefix, ns_object))
32
- log.info "completions: #{response["completions"]}"
27
+ if prefix.nil? || parse_string(prefix).nil?
28
+ response.set("completions", [])
29
+ else
30
+ ns_object = if ns.empty?
31
+ ::Object
32
+ else
33
+ @workspace.lookup_module(ns)
34
+ end
33
35
 
36
+ # If the prefix isn't parseable, return an empty list
37
+ response.set("completions", lookup_module(prefix, klass: ns_object, completion_fn: complete_fn))
38
+ end
34
39
  response.status("done")
35
40
  response
36
- rescue ScriptError, StandardError => e
37
- log.error "Error in completions: #{e}"
38
- log.error e.backtrace.join("\n")
39
- raise e
40
41
  end
41
42
 
42
- private
43
-
44
- def lookup_module(module_name, klass = ::Object)
43
+ def lookup_module(module_name, klass: ::Object, completion_fn: "top-level")
45
44
  node = parse_string(module_name)
46
45
  case node.type
47
46
  when :const
48
47
  ns = lookup_module_ast(node.children.first, klass)
49
48
  prefix = node.children.last
50
- all_constants(ns, prefix) + all_methods(ns, prefix)
49
+ all_constants(ns, prefix)
51
50
  when :send
52
51
  ns = lookup_module_ast(node.children.first, klass)
53
52
  prefix = node.children.last
54
- all_methods(ns, prefix)
53
+ if completion_fn == "method"
54
+ all_constants(ns, prefix) + encode_methods(class_instance_methods(ns, prefix) + class_singleton_methods(::Kernel, prefix))
55
+ else
56
+ all_constants(ns, prefix) + encode_methods(class_methods(ns, prefix) + class_singleton_methods(::Kernel, prefix))
57
+ end
55
58
  else
56
59
  []
57
60
  end
@@ -64,6 +67,8 @@ module RiderServer
64
67
  end
65
68
 
66
69
  def lookup_module_ast(node, klass = ::Object)
70
+ return klass if node.nil?
71
+
67
72
  case node.type
68
73
  when :cbase
69
74
  ::Object
@@ -78,17 +83,57 @@ module RiderServer
78
83
  end
79
84
  end
80
85
 
86
+ def parent_constant(ns_object)
87
+ ns_object_name = ns_object.to_s
88
+ parent_name, _ = ns_object_name.rpartition("::")
89
+ parent_name.empty? ? nil : ::Object.const_get(parent_name)
90
+ end
91
+
81
92
  def all_constants(ns_object, prefix)
82
- ns_object.constants.grep(/^#{prefix}/).map do |constant|
93
+ consts = []
94
+ ns_object.constants.grep(/^#{prefix}/).each do |constant|
95
+ consts << constant.to_s
96
+ end
97
+
98
+ # Search up the tree to find all the other constants
99
+ constant = ns_object
100
+ loop do
101
+ constant = parent_constant(constant)
102
+ break if constant.nil?
103
+
104
+ constant.constants.grep(/^#{prefix}/).each do |constant|
105
+ consts << constant.to_s
106
+ end
107
+ end
108
+
109
+ consts.map do |constant|
83
110
  {
84
- "candidate" => constant.to_s,
111
+ "candidate" => constant,
85
112
  "type" => "constant"
86
113
  }
87
114
  end
88
115
  end
89
116
 
90
- def all_methods(ns_object, prefix)
91
- ns_object.methods.grep(/^#{prefix}/).map do |method|
117
+ ##
118
+ # Get all instance methods that match a +prefix+ for a given object +obj+
119
+ #
120
+ # This method should be called when in the context of an
121
+ # instance method, as all other instance methods will be
122
+ # acessable.
123
+ def class_instance_methods(obj, prefix)
124
+ Set.new(obj.instance_methods.grep(/^#{prefix}/))
125
+ end
126
+
127
+ def class_methods(obj, prefix)
128
+ Set.new(obj.methods.grep(/^#{prefix}/))
129
+ end
130
+
131
+ def class_singleton_methods(obj, prefix)
132
+ Set.new(obj.singleton_methods.grep(/^#{prefix}/))
133
+ end
134
+
135
+ def encode_methods(methods)
136
+ methods.map do |method|
92
137
  {
93
138
  "candidate" => method.to_s,
94
139
  "type" => "method"
@@ -46,8 +46,8 @@ module RiderServer
46
46
  response.set("ex", e.inspect)
47
47
  response.set("ns", session.workspace.evaluate("inspect"))
48
48
  response.status("eval-error", "done")
49
- exception_id = session.add_exception(operation["id"], e)
50
- response.set("rider/exception-id", exception_id)
49
+ exception = session.push_exception(operation["id"], e)
50
+ response.set("rider/exception-id", exception["id"])
51
51
  send_response(response)
52
52
  end
53
53
 
@@ -5,10 +5,11 @@ require "rider_server/inspect"
5
5
  module RiderServer
6
6
  module Ops
7
7
  class Lookup < Operation
8
- documentation "Get completions for a given string"
8
+ documentation "Get info about a symbol."
9
9
 
10
10
  argument :id, :string, "The request id", required: true
11
11
  argument :sym, :string, "The symbol to lookup", required: true
12
+ argument :lookup_fn, :string, "What lookup function to use, top-level, module, method or singleton-method"
12
13
  argument :ns, :string, "The namespace to search for completions"
13
14
 
14
15
  def initialize(*args)
@@ -20,63 +21,83 @@ module RiderServer
20
21
  response = Response.new(operation)
21
22
  ns = operation["ns"]
22
23
  sym = operation["sym"]
24
+ lookup_fn = operation.fetch("lookup-fn", "top-level")
23
25
 
24
- ns_object = @workspace.lookup_module(ns)
25
-
26
- method_type = nil
27
- if sym.include?("#")
28
- sym, method = sym.split("#")
29
- method_type = :instance
30
- method = method.to_sym
31
- elsif sym.start_with?("self.class")
32
- method = sym.split(".").last
33
- method_type = :class
34
- method = method.to_sym
35
- elsif sym.include?(".")
36
- sym, method = sym.split(".")
37
- method_type = :class
38
- method = method.to_sym
39
- end
26
+ if sym.nil? || parse_string(sym).nil?
27
+ response.set("info", {})
28
+ else
29
+ ns_object = if ns.empty?
30
+ ::Object
31
+ else
32
+ @workspace.lookup_module(ns)
33
+ end
40
34
 
41
- if sym.include?("::")
42
- ns_object = lookup_ns(sym.split("::"), ns_object)
35
+ # If the prefix isn't parseable, return an empty list
36
+ response.set("info", lookup_module(sym, klass: ns_object, lookup_fn: lookup_fn))
43
37
  end
44
- constant = ns_object
38
+ response.status("done")
39
+ response
40
+ end
45
41
 
46
- info = if sym.start_with?("@")
47
- constant = ns_object.instance_variable_get(sym)
48
- elsif method_type == :instance
49
- RiderServer::Inspect.describe_method(constant.instance_method(method))
50
- elsif method_type == :class
51
- RiderServer::Inspect.describe_method(constant.method(method))
52
- elsif constant.respond_to?(:instance_methods) && constant.instance_methods.include?(sym.to_sym)
53
- RiderServer::Inspect.describe_method(constant.instance_method(sym))
54
- elsif constant.respond_to?(:methods) && constant.methods.include?(sym.to_sym)
55
- RiderServer::Inspect.describe_method(constant.method(sym))
42
+ def lookup_module(module_name, klass: ::Object, lookup_fn: "top-level")
43
+ node = parse_string(module_name)
44
+ case node.type
45
+ when :const
46
+ ns = lookup_module_ast(node.children.first, klass)
47
+ name = node.children.last
48
+ encode_const(ns, name)
49
+ when :send
50
+ ns = lookup_module_ast(node.children.first, klass)
51
+ prefix = node.children.last
52
+ if lookup_fn == "method"
53
+ encode_method(ns.instance_method(prefix))
54
+ else
55
+ encode_method(ns.method(prefix))
56
+ end
56
57
  else
57
- {}
58
+ []
58
59
  end
60
+ end
59
61
 
60
- response.set("info", info)
61
- response.status("done")
62
- response
62
+ def parse_string(string)
63
+ buffer = Parser::Source::Buffer.new("(string)")
64
+ buffer.source = string
65
+ Parser::CurrentRuby.new.parse(buffer)
63
66
  end
64
67
 
65
- private
68
+ def lookup_module_ast(node, klass = ::Object)
69
+ return klass if node.nil?
66
70
 
67
- def lookup_ns(module_name, klass = Object)
68
- if module_name.start_with?("::")
69
- klass = Module
70
- module_name = module_name.delete_prefix("::")
71
+ case node.type
72
+ when :cbase
73
+ ::Object
74
+ when :const
75
+ if node.children.first.nil?
76
+ klass.const_get(node.children.last)
77
+ else
78
+ lookup_module_ast(node.children.first, klass).const_get(node.children.last)
79
+ end
80
+ else
81
+ raise ModuleLookupError, "Unknown node type #{node.type}"
71
82
  end
83
+ end
72
84
 
73
- path = module_name.split("::")
74
-
75
- path.inject(klass) do |acc, elem|
76
- acc.const_get(elem)
85
+ def encode_const(ns, const_name)
86
+ if ns.const_defined? const_name
87
+ {
88
+ "name" => "#{ns}::#{const_name}",
89
+ "source_location" => ns.const_source_location(const_name)
90
+ }
91
+ else
92
+ {}
77
93
  end
78
- rescue NameError
79
- nil
94
+ end
95
+
96
+ def encode_method(thing)
97
+ {
98
+ "name" => thing.name.to_s,
99
+ "source_location" => thing.source_location
100
+ }
80
101
  end
81
102
  end
82
103
  end
@@ -4,7 +4,7 @@ require "rider_server/response"
4
4
  module RiderServer
5
5
  module Ops
6
6
  class LsExceptions < Operation
7
- documentation "List all exceptions that have occurred in the session."
7
+ documentation "List all exceptions that exist in the sessions cache."
8
8
 
9
9
  argument :id, :string, "The request id", required: true
10
10
 
@@ -13,7 +13,7 @@ module RiderServer
13
13
  def handle(session, operation)
14
14
  response = Response.new(operation)
15
15
  name = operation["name"]
16
- raise "Name must be a valid Ruby identifier" unless name =~ /\A[a-zA-Z_]\w*\z/
16
+ raise "Name must be a valid Ruby identifier" unless /\A[a-zA-Z_]\w*\z/.match?(name)
17
17
  location = operation["location"]
18
18
 
19
19
  value = traverse_location(location, nil, session)
@@ -57,7 +57,7 @@ module RiderServer
57
57
 
58
58
  begin
59
59
  output = response.bencode
60
- rescue StandardError => e
60
+ rescue => e
61
61
  log.error "Error encoding response: #{e}"
62
62
  response = Response.new(response.to_h)
63
63
  response.set("ex", e.inspect)
@@ -28,11 +28,27 @@ module RiderServer
28
28
  }
29
29
  end
30
30
 
31
- def self.handle_exception(exception, metadata = {})
31
+ def self.create_exception_wrapper(exception, metadata)
32
+ id = SecureRandom.uuid
33
+ {
34
+ "id" => id,
35
+ "created_at" => DateTime.now,
36
+ "exception" => exception,
37
+ "metadata" => metadata
38
+ }
39
+ end
40
+
41
+ def self.handle_exception(exception, metadata: {})
42
+ wrapped_ex = create_exception_wrapper(exception, metadata)
43
+ # Firstly this needs to generate wrapper for the exception
44
+ # This needs to use push exception
45
+
32
46
  @sessions.each do |session, stream_id|
33
- ex = session.add_exception(stream_id, exception, metadata)
47
+ ex = session.push_anonymous_exception(wrapped_ex)
34
48
  session.response_queue.push(create_response(stream_id, ex))
35
49
  end
50
+
51
+ wrapped_ex
36
52
  end
37
53
 
38
54
  def initialize(session)
@@ -93,15 +93,20 @@ module RiderServer
93
93
  # Historical Exceptions
94
94
  #
95
95
 
96
- def add_exception(operation_id, exception, metadata = {})
96
+ def wrap_exception(operation_id, exception, metadata = {})
97
97
  id = SecureRandom.uuid
98
- @exceptions << {
98
+ {
99
99
  "id" => id,
100
100
  "operation_id" => operation_id,
101
101
  "created_at" => DateTime.now,
102
102
  "exception" => exception,
103
103
  "metadata" => metadata
104
104
  }
105
+ end
106
+
107
+ def add_exception(operation_id, exception, metadata = {})
108
+ id = SecureRandom.uuid
109
+ @exceptions << wrap_exception(operation_id, exception, metadata)
105
110
  id
106
111
  end
107
112
 
@@ -113,7 +118,14 @@ module RiderServer
113
118
 
114
119
  # A threadsafe way to add exceptions
115
120
  def push_exception(operation_id, exception, metadata = {})
116
- @exception_queue.push([operation_id, exception, metadata])
121
+ ex = wrap_exception(operation_id, exception, metadata)
122
+ @exception_queue.push(ex)
123
+ ex
124
+ end
125
+
126
+ def push_anonymous_exception(exception)
127
+ @exception_queue.push(exception)
128
+ exception
117
129
  end
118
130
 
119
131
  def start_exception_processing
@@ -121,7 +133,7 @@ module RiderServer
121
133
 
122
134
  @exception_processing_thread = Thread.new do
123
135
  loop do
124
- add_exception(*@exception_queue.pop)
136
+ @exceptions << @exception_queue.pop
125
137
  end
126
138
  end
127
139
  end
@@ -8,5 +8,5 @@
8
8
  # SPDX-License-Identifier: MIT
9
9
 
10
10
  module RiderServer
11
- VERSION = "0.1.0"
11
+ VERSION = "0.1.1"
12
12
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rider-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Russell Sim
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-27 00:00:00.000000000 Z
11
+ date: 2024-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bencode
@@ -50,6 +50,7 @@ files:
50
50
  - ".ruby-version"
51
51
  - ".standard.yml"
52
52
  - CHANGELOG.md
53
+ - COPYING
53
54
  - README.md
54
55
  - Rakefile
55
56
  - exe/rider-server