Class: RubyLsp::Addon Abstract
- Inherits:
-
Object
- Object
- RubyLsp::Addon
- Defined in:
- lib/ruby_lsp/addon.rb
Overview
To register an add-on, inherit from this class and implement both ‘name` and `activate`
# Example
“‘ruby module MyGem
class MyAddon < Addon
def activate
# Perform any relevant initialization
end
def name
"My add-on name"
end
end
end “‘
Defined Under Namespace
Classes: IncompatibleApiError
Constant Summary collapse
- AddonNotFoundError =
: Array
Class.new(StandardError)
Class Attribute Summary collapse
-
.addon_classes ⇒ Object
readonly
: Array.
-
.addons ⇒ Object
: Array.
-
.file_watcher_addons ⇒ Object
: Array.
Class Method Summary collapse
-
.depend_on_ruby_lsp!(*version_constraints) ⇒ Object
Depend on a specific version of the Ruby LSP.
-
.get(addon_name, *version_constraints) ⇒ Object
Get a reference to another add-on object by name and version.
-
.inherited(child_class) ⇒ Object
Automatically track and instantiate add-on classes : (singleton(Addon) child_class) -> void.
-
.load_addons(global_state, outgoing_queue, include_project_addons: true) ⇒ Object
Discovers and loads all add-ons.
-
.unload_addons ⇒ Object
Unloads all add-ons.
Instance Method Summary collapse
-
#activate(global_state, outgoing_queue) ⇒ Object
abstract
Each add-on should implement ‘MyAddon#activate` and use to perform any sort of initialization, such as reading information into memory or even spawning a separate process : (GlobalState, Thread::Queue) -> void.
-
#add_error(error) ⇒ Object
: (StandardError error) -> self.
-
#create_code_lens_listener(response_builder, uri, dispatcher) ⇒ Object
Creates a new CodeLens listener.
-
#create_completion_listener(response_builder, node_context, dispatcher, uri) ⇒ Object
Creates a new Completion listener.
-
#create_definition_listener(response_builder, uri, node_context, dispatcher) ⇒ Object
Creates a new Definition listener.
-
#create_discover_tests_listener(response_builder, dispatcher, uri) ⇒ Object
Creates a new Discover Tests listener.
-
#create_document_symbol_listener(response_builder, dispatcher) ⇒ Object
Creates a new DocumentSymbol listener.
-
#create_hover_listener(response_builder, node_context, dispatcher) ⇒ Object
Creates a new Hover listener.
-
#create_semantic_highlighting_listener(response_builder, dispatcher) ⇒ Object
: (ResponseBuilders::SemanticHighlighting response_builder, Prism::Dispatcher dispatcher) -> void.
-
#deactivate ⇒ Object
abstract
Each add-on must implement ‘MyAddon#deactivate` and use to perform any clean up, like shutting down a child process : -> void.
-
#error? ⇒ Boolean
: -> bool.
-
#errors_details ⇒ Object
: -> String.
-
#formatted_errors ⇒ Object
: -> String.
-
#handle_window_show_message_response(title) ⇒ Object
Handle a response from a window/showMessageRequest request.
-
#initialize ⇒ Addon
constructor
: -> void.
-
#name ⇒ Object
abstract
Add-ons should override the ‘name` method to return the add-on name : -> String.
-
#resolve_test_commands(items) ⇒ Object
Resolves the minimal set of commands required to execute the requested tests.
-
#version ⇒ Object
abstract
Add-ons should override the ‘version` method to return a semantic version string representing the add-on’s version.
Constructor Details
#initialize ⇒ Addon
: -> void
168 169 170 |
# File 'lib/ruby_lsp/addon.rb', line 168 def initialize @errors = [] #: Array[StandardError] end |
Class Attribute Details
.addon_classes ⇒ Object (readonly)
: Array
41 42 43 |
# File 'lib/ruby_lsp/addon.rb', line 41 def addon_classes @addon_classes end |
.file_watcher_addons ⇒ Object
: Array
38 39 40 |
# File 'lib/ruby_lsp/addon.rb', line 38 def file_watcher_addons @file_watcher_addons end |
Class Method Details
.depend_on_ruby_lsp!(*version_constraints) ⇒ Object
Depend on a specific version of the Ruby LSP. This method should only be used if the add-on is distributed in a gem that does not have a runtime dependency on the ruby-lsp gem. This method should be invoked at the top of the ‘addon.rb` file before defining any classes or requiring any files. For example:
“‘ruby RubyLsp::Addon.depend_on_ruby_lsp!(“>= 0.18.0”)
module MyGem
class MyAddon < RubyLsp::Addon
# ...
end
end “‘ : (*String version_constraints) -> void
157 158 159 160 161 162 163 164 |
# File 'lib/ruby_lsp/addon.rb', line 157 def depend_on_ruby_lsp!(*version_constraints) version_object = Gem::Version.new(RubyLsp::VERSION) unless version_constraints.all? { |constraint| Gem::Requirement.new(constraint).satisfied_by?(version_object) } raise IncompatibleApiError, "Add-on is not compatible with this version of the Ruby LSP. Skipping its activation" end end |
.get(addon_name, *version_constraints) ⇒ Object
Get a reference to another add-on object by name and version. If an add-on exports an API that can be used by other add-ons, this is the way to get access to that API.
Important: if the add-on is not found, AddonNotFoundError will be raised. If the add-on is found, but its current version does not satisfy the given version constraint, then IncompatibleApiError will be raised. It is the responsibility of the add-ons using this API to handle these errors appropriately. : (String addon_name, *String version_constraints) -> Addon
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/ruby_lsp/addon.rb', line 125 def get(addon_name, *version_constraints) if version_constraints.empty? raise IncompatibleApiError, "Must specify version constraints when accessing other add-ons" end addon = addons.find { |addon| addon.name == addon_name } raise AddonNotFoundError, "Could not find add-on '#{addon_name}'" unless addon version_object = Gem::Version.new(addon.version) unless version_constraints.all? { |constraint| Gem::Requirement.new(constraint).satisfied_by?(version_object) } raise IncompatibleApiError, "Constraints #{version_constraints.inspect} is incompatible with #{addon_name} version #{addon.version}" end addon end |
.inherited(child_class) ⇒ Object
Automatically track and instantiate add-on classes : (singleton(Addon) child_class) -> void
45 46 47 48 |
# File 'lib/ruby_lsp/addon.rb', line 45 def inherited(child_class) addon_classes << child_class super end |
.load_addons(global_state, outgoing_queue, include_project_addons: true) ⇒ Object
Discovers and loads all add-ons. Returns a list of errors when trying to require add-ons : (GlobalState global_state, Thread::Queue outgoing_queue, ?include_project_addons: bool) -> Array
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/ruby_lsp/addon.rb', line 52 def load_addons(global_state, outgoing_queue, include_project_addons: true) # Require all add-ons entry points, which should be placed under # `some_gem/lib/ruby_lsp/your_gem_name/addon.rb` or in the workspace under # `your_project/ruby_lsp/project_name/addon.rb` addon_files = Gem.find_files("ruby_lsp/**/addon.rb") if include_project_addons project_addons = Dir.glob("#{global_state.workspace_path}/**/ruby_lsp/**/addon.rb") bundle_path = Bundler.bundle_path.to_s gems_dir = Bundler.bundle_path.join("gems") # Create an array of rejection glob patterns to ignore add-ons already discovered through Gem.find_files if # they are also copied inside the workspace for whatever reason. We received reports of projects having gems # installed in vendor/bundle despite BUNDLE_PATH pointing elsewhere. Without this mechanism, we will # double-require the same add-on, potentially for different versions of the same gem, which leads to incorrect # behavior reject_glob_patterns = addon_files.map do |path| relative_gem_path = Pathname.new(path).relative_path_from(gems_dir) first_part, *parts = relative_gem_path.to_s.split(File::SEPARATOR) first_part&.gsub!(/-([0-9.]+)$/, "*") "**/#{first_part}/#{parts.join("/")}" end project_addons.reject! do |path| path.start_with?(bundle_path) || reject_glob_patterns.any? { |pattern| File.fnmatch?(pattern, path, File::Constants::FNM_PATHNAME) } end addon_files.concat(project_addons) end errors = addon_files.filter_map do |addon_path| # Avoid requiring this file twice. This may happen if you're working on the Ruby LSP itself and at the same # time have `ruby-lsp` installed as a vendored gem next if File.basename(File.dirname(addon_path)) == "ruby_lsp" require File.(addon_path) nil rescue => e e end # Instantiate all discovered add-on classes self.addons = addon_classes.map(&:new) self.file_watcher_addons = addons.select { |addon| addon.respond_to?(:workspace_did_change_watched_files) } # Activate each one of the discovered add-ons. If any problems occur in the add-ons, we don't want to # fail to boot the server addons.each do |addon| addon.activate(global_state, outgoing_queue) rescue => e addon.add_error(e) end errors end |
.unload_addons ⇒ Object
Unloads all add-ons. Only intended to be invoked once when shutting down the Ruby LSP server : -> void
111 112 113 114 115 116 |
# File 'lib/ruby_lsp/addon.rb', line 111 def unload_addons @addons.each(&:deactivate) @addons.clear @addon_classes.clear @file_watcher_addons.clear end |
Instance Method Details
#activate(global_state, outgoing_queue) ⇒ Object
Each add-on should implement ‘MyAddon#activate` and use to perform any sort of initialization, such as reading information into memory or even spawning a separate process : (GlobalState, Thread::Queue) -> void
200 201 202 |
# File 'lib/ruby_lsp/addon.rb', line 200 def activate(global_state, outgoing_queue) raise AbstractMethodInvokedError end |
#add_error(error) ⇒ Object
: (StandardError error) -> self
173 174 175 176 |
# File 'lib/ruby_lsp/addon.rb', line 173 def add_error(error) @errors << error self end |
#create_code_lens_listener(response_builder, uri, dispatcher) ⇒ Object
Creates a new CodeLens listener. This method is invoked on every CodeLens request : (ResponseBuilders::CollectionResponseBuilder response_builder, URI::Generic uri, Prism::Dispatcher dispatcher) -> void
238 |
# File 'lib/ruby_lsp/addon.rb', line 238 def create_code_lens_listener(response_builder, uri, dispatcher); end |
#create_completion_listener(response_builder, node_context, dispatcher, uri) ⇒ Object
Creates a new Completion listener. This method is invoked on every Completion request : (ResponseBuilders::CollectionResponseBuilder response_builder, NodeContext node_context, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
262 |
# File 'lib/ruby_lsp/addon.rb', line 262 def create_completion_listener(response_builder, node_context, dispatcher, uri); end |
#create_definition_listener(response_builder, uri, node_context, dispatcher) ⇒ Object
Creates a new Definition listener. This method is invoked on every Definition request : (ResponseBuilders::CollectionResponseBuilder[(Interface::Location | Interface::LocationLink)] response_builder, URI::Generic uri, NodeContext node_context, Prism::Dispatcher dispatcher) -> void
257 |
# File 'lib/ruby_lsp/addon.rb', line 257 def create_definition_listener(response_builder, uri, node_context, dispatcher); end |
#create_discover_tests_listener(response_builder, dispatcher, uri) ⇒ Object
Creates a new Discover Tests listener. This method is invoked on every DiscoverTests request : (ResponseBuilders::TestCollection response_builder, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
267 |
# File 'lib/ruby_lsp/addon.rb', line 267 def create_discover_tests_listener(response_builder, dispatcher, uri); end |
#create_document_symbol_listener(response_builder, dispatcher) ⇒ Object
Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request : (ResponseBuilders::DocumentSymbol response_builder, Prism::Dispatcher dispatcher) -> void
248 |
# File 'lib/ruby_lsp/addon.rb', line 248 def create_document_symbol_listener(response_builder, dispatcher); end |
#create_hover_listener(response_builder, node_context, dispatcher) ⇒ Object
Creates a new Hover listener. This method is invoked on every Hover request : (ResponseBuilders::Hover response_builder, NodeContext node_context, Prism::Dispatcher dispatcher) -> void
243 |
# File 'lib/ruby_lsp/addon.rb', line 243 def create_hover_listener(response_builder, node_context, dispatcher); end |
#create_semantic_highlighting_listener(response_builder, dispatcher) ⇒ Object
: (ResponseBuilders::SemanticHighlighting response_builder, Prism::Dispatcher dispatcher) -> void
252 |
# File 'lib/ruby_lsp/addon.rb', line 252 def create_semantic_highlighting_listener(response_builder, dispatcher); end |
#deactivate ⇒ Object
Each add-on must implement ‘MyAddon#deactivate` and use to perform any clean up, like shutting down a child process : -> void
208 209 210 |
# File 'lib/ruby_lsp/addon.rb', line 208 def deactivate raise AbstractMethodInvokedError end |
#error? ⇒ Boolean
: -> bool
179 180 181 |
# File 'lib/ruby_lsp/addon.rb', line 179 def error? @errors.any? end |
#errors_details ⇒ Object
: -> String
192 193 194 |
# File 'lib/ruby_lsp/addon.rb', line 192 def errors_details @errors.map(&:full_message).join("\n\n") end |
#formatted_errors ⇒ Object
: -> String
184 185 186 187 188 189 |
# File 'lib/ruby_lsp/addon.rb', line 184 def formatted_errors " \#{name}:\n \#{@errors.map(&:message).join(\"\\n\")}\n ERRORS\nend\n" |
#handle_window_show_message_response(title) ⇒ Object
Handle a response from a window/showMessageRequest request. Add-ons must include the addon_name as part of the original request so that the response is delegated to the correct add-on and must override this method to handle the response microsoft.github.io/language-server-protocol/specification#window_showMessageRequest : (String title) -> void
233 |
# File 'lib/ruby_lsp/addon.rb', line 233 def (title); end |
#name ⇒ Object
Add-ons should override the ‘name` method to return the add-on name : -> String
215 216 217 |
# File 'lib/ruby_lsp/addon.rb', line 215 def name raise AbstractMethodInvokedError end |
#resolve_test_commands(items) ⇒ Object
Resolves the minimal set of commands required to execute the requested tests. Add-ons are responsible for only handling items related to the framework they add support for and have discovered themselves : (Array[Hash[Symbol, untyped]]) -> Array
273 274 275 |
# File 'lib/ruby_lsp/addon.rb', line 273 def resolve_test_commands(items) [] end |
#version ⇒ Object
Add-ons should override the ‘version` method to return a semantic version string representing the add-on’s version. This is used for compatibility checks : -> String
223 224 225 |
# File 'lib/ruby_lsp/addon.rb', line 223 def version raise AbstractMethodInvokedError end |