Skip to content

Commit

Permalink
Implement LSP runner
Browse files Browse the repository at this point in the history
  • Loading branch information
andyw8 committed Feb 14, 2024
1 parent a600e02 commit 01047e0
Showing 12 changed files with 30 additions and 371 deletions.
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -52,16 +52,15 @@ See the [documentation](https://shopify.github.io/ruby-lsp-rails) for more in-de
## How It Works

This gem consists of two components that enable enhanced Rails functionality in the editor:
When Ruby LSP Rails starts, it spawns a `rails runner` instance which runs
`[server.rb](https://github.com/Shopify/ruby-lsp-rails/blob/main/lib/ruby_lsp/ruby_lsp_rails/server.rb)`.
The addon communicates with this process over a pipe (i.e. `stdin` and `stdout`) to fetch runtime information about the application.

1. A Rack app that automatically exposes APIs when Rails server is running
1. A Ruby LSP addon that connects to the exposed APIs to fetch runtime information from the Rails server

This is why the Rails server needs to be running for some features to work.
When extension is stopped (e.g. by quitting the editor), the server instance is shut down.

> [!NOTE]
> There is no need to restart the Ruby LSP every time the Rails server is booted.
> If the server is shut down, the extra features will temporarily disappear and reappear once the server is running again.
> Prior to v0.3, `ruby-lsp-rails` used a different approach which involved mounting a Rack application with in the Rails app.
> That approach was brittle and susceptible to the application's configuration, such as routing and middleware.
## Contributing

4 changes: 0 additions & 4 deletions lib/ruby-lsp-rails.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
# typed: strict
# frozen_string_literal: true

require "sorbet-runtime"
require "pathname"

require "ruby_lsp_rails/version"
require "ruby_lsp_rails/railtie"

module RubyLsp
# # Supported features
14 changes: 7 additions & 7 deletions lib/ruby_lsp/ruby_lsp_rails/addon.rb
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@

require "ruby_lsp/addon"

require_relative "rails_client"
require_relative "runner_client"
require_relative "hover"
require_relative "code_lens"

@@ -12,18 +12,18 @@ module Rails
class Addon < ::RubyLsp::Addon
extend T::Sig

sig { returns(RailsClient) }
sig { returns(RunnerClient) }
def client
@client ||= T.let(RailsClient.new, T.nilable(RailsClient))
@client ||= T.let(RunnerClient.new, T.nilable(RunnerClient))
end

sig { override.params(message_queue: Thread::Queue).void }
def activate(message_queue)
client.check_if_server_is_running!
end
def activate(message_queue); end

sig { override.void }
def deactivate; end
def deactivate
client.shutdown
end

# Creates a new CodeLens listener. This method is invoked on every CodeLens request
sig do
2 changes: 1 addition & 1 deletion lib/ruby_lsp/ruby_lsp_rails/hover.rb
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ class Hover

sig do
params(
client: RailsClient,
client: RunnerClient,
response_builder: ResponseBuilders::Hover,
nesting: T::Array[String],
index: RubyIndexer::Index,
77 changes: 0 additions & 77 deletions lib/ruby_lsp/ruby_lsp_rails/rails_client.rb

This file was deleted.

58 changes: 0 additions & 58 deletions lib/ruby_lsp_rails/rack_app.rb

This file was deleted.

38 changes: 0 additions & 38 deletions lib/ruby_lsp_rails/railtie.rb

This file was deleted.

13 changes: 0 additions & 13 deletions test/ruby_lsp_rails/addon_test.rb
Original file line number Diff line number Diff line change
@@ -10,19 +10,6 @@ class AddonTest < ActiveSupport::TestCase
addon = Addon.new
assert_equal("Ruby LSP Rails", addon.name)
end

test "activate checks if Rails server is running" do
rails_client = stub("rails_client", check_if_server_is_running!: true)

RubyLsp::Rails::RailsClient.stubs(instance: rails_client)

addon = Addon.new
queue = Thread::Queue.new

capture_io { assert(addon.activate(queue)) }
ensure
T.must(queue).close
end
end
end
end
31 changes: 15 additions & 16 deletions test/ruby_lsp_rails/hover_test.rb
Original file line number Diff line number Diff line change
@@ -2,13 +2,12 @@
# frozen_string_literal: true

require "test_helper"
# require "ruby_lsp/ruby_lsp_rails/server"

module RubyLsp
module Rails
class HoverTest < ActiveSupport::TestCase
setup do
File.write("#{Dir.pwd}/test/dummy/tmp/app_uri.txt", "http://localhost:3000")
@client = RailsClient.new
@message_queue = Thread::Queue.new

# Build the Rails documents index ahead of time
@@ -23,7 +22,7 @@ class HoverTest < ActiveSupport::TestCase

test "hook returns model column information" do
expected_response = {
schema_file: "#{@client.root}/db/schema.rb",
schema_file: "#{dummy_root}/db/schema.rb",
columns: [
["id", "integer"],
["first_name", "string"],
@@ -34,8 +33,7 @@ class HoverTest < ActiveSupport::TestCase
],
}

stub_http_request("200", expected_response.to_json)
@client.stubs(:check_if_server_is_running!)
RunnerClient.any_instance.stubs(model: expected_response)

response = hover_on_source(<<~RUBY, { line: 3, character: 0 })
class User < ApplicationRecord
@@ -50,7 +48,7 @@ class User < ApplicationRecord
```
**Definitions**: [fake.rb](file:///fake.rb#L1,1-2,4)
[Schema](file://#{@client.root}/db/schema.rb)
[Schema](file://#{dummy_root}/db/schema.rb)
**id**: integer
@@ -69,7 +67,7 @@ class User < ApplicationRecord

test "return column information for namespaced models" do
expected_response = {
schema_file: "#{@client.root}/db/schema.rb",
schema_file: "#{dummy_root}/db/schema.rb",
columns: [
["id", "integer"],
["first_name", "string"],
@@ -80,8 +78,7 @@ class User < ApplicationRecord
],
}

stub_http_request("200", expected_response.to_json)
@client.stubs(:check_if_server_is_running!)
RunnerClient.any_instance.stubs(model: expected_response)

response = hover_on_source(<<~RUBY, { line: 4, character: 6 })
module Blog
@@ -93,7 +90,7 @@ class User < ApplicationRecord
RUBY

assert_equal(<<~CONTENT.chomp, response.contents.value)
[Schema](file://#{@client.root}/db/schema.rb)
[Schema](file://#{dummy_root}/db/schema.rb)
**id**: integer
@@ -111,12 +108,11 @@ class User < ApplicationRecord

test "handles `db/structure.sql` instead of `db/schema.rb`" do
expected_response = {
schema_file: "#{@client.root}/db/structure.sql",
schema_file: "#{dummy_root}/db/structure.sql",
columns: [],
}

stub_http_request("200", expected_response.to_json)
@client.stubs(:check_if_server_is_running!)
RunnerClient.any_instance.stubs(model: expected_response)

response = hover_on_source(<<~RUBY, { line: 3, character: 0 })
class User < ApplicationRecord
@@ -127,7 +123,7 @@ class User < ApplicationRecord

assert_includes(
response.contents.value,
"[Schema](file://#{@client.root}/db/structure.sql)",
"[Schema](file://#{dummy_root}/db/structure.sql)",
)
end

@@ -137,8 +133,7 @@ class User < ApplicationRecord
columns: [],
}

stub_http_request("200", expected_response.to_json)
@client.stubs(:check_if_server_is_running!)
RunnerClient.any_instance.stubs(model: expected_response)

response = hover_on_source(<<~RUBY, { line: 3, character: 0 })
class User < ApplicationRecord
@@ -213,6 +208,10 @@ def hover_on_source(source, position)
assert_nil(response.error)
response.response
end

def dummy_root
File.expand_path("#{__dir__}/../../test/dummy")
end
end
end
end
Loading

0 comments on commit 01047e0

Please sign in to comment.