Skip to content
Merged
25 changes: 13 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.PHONY: image guard continuous_development console test shell run_in_shell server gem gem_release publish_cross_platform_image
PROJECT_NAME=ruby_language_server
LOCAL_LINK=-v $(PWD):/tmp/src -w /tmp/src

Expand All @@ -9,7 +10,7 @@ force_rebuild_image:

guard: image
echo > active_record.log
docker run -it --rm $(LOCAL_LINK) -e LOG_LEVEL=DEBUG $(PROJECT_NAME) bundle exec guard
./bin/run_in_shell bundle exec guard
echo > active_record.log

continuous_development: image
Expand All @@ -23,30 +24,30 @@ continuous_development: image
done

console: image
docker run -it --rm $(LOCAL_LINK) $(PROJECT_NAME) bin/console
./bin/run_in_shell bin/console

test: image
docker run --rm $(LOCAL_LINK) $(PROJECT_NAME) sh -c 'bundle exec rake test && bundle exec rubocop'
./bin/run_in_shell "bundle exec rake test && bundle exec rubocop"

shell: image
docker run -it --rm $(LOCAL_LINK) $(PROJECT_NAME) sh
./bin/run_in_shell sh

run_in_shell: image
docker run -it --rm $(LOCAL_LINK) $(PROJECT_NAME) sh -c '$(COMMAND)'
run_in_shell:
docker run -it --rm $(LOCAL_LINK) $(PROJECT_NAME) sh -c "${SHELL_COMMAND}"

# Just to make sure it works.
server: image
docker run -it --rm $(LOCAL_LINK) $(PROJECT_NAME)
./bin/run_in_shell

gem: image
rm -f $(PROJECT_NAME)*.gem
docker run $(LOCAL_LINK) $(PROJECT_NAME) gem build $(PROJECT_NAME)
./bin/run_in_shell gem build $(PROJECT_NAME)

# Requires rubygems be installed on host
gem_release: gem
docker run -it --rm $(LOCAL_LINK) $(PROJECT_NAME) gem push $(PROJECT_NAME)*.gem
./bin/run_in_shell gem push $(PROJECT_NAME)*.gem

publish_cross_platform_image:
(docker buildx ls | grep mybuilder) || docker buildx create --name mybuilder
docker buildx use mybuilder
docker buildx build --push --platform linux/amd64,linux/arm64/v8 -t kwerle/$(PROJECT_NAME) .
(docker buildx ls | grep mybuilder) || ./bin/run_in_shell docker buildx create --name mybuilder
./bin/run_in_shell docker buildx use mybuilder
./bin/run_in_shell docker buildx build --push --platform linux/amd64,linux/arm64/v8 -t kwerle/$(PROJECT_NAME) .
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ Help welcome.

* Definitions
* Completions
* Lint - thanks to [RuboCop](https://github.com/bbatsov/rubocop)
* Please see the [FAQ_ROADMAP.md](./FAQ_ROADMAP.md)

# Editor Integrations
Expand Down
5 changes: 5 additions & 0 deletions bin/run_in_shell
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
# Mostly so AI can run commands in a consistent environment
SHELL_COMMAND="$*"
export SHELL_COMMAND
make run_in_shell
21 changes: 8 additions & 13 deletions lib/config/initializers/active_record.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
# frozen_string_literal: true

extension_path = if Gem.win_platform?
'liblevenshtein.dll'
else
'/usr/local/lib/liblevenshtein.so.0.0.0'
end

ActiveRecord::Base.establish_connection(
adapter: 'sqlite3',
database: '/database',
pool: 5,
timeout: 30.seconds # does not seem to help
timeout: 30.seconds, # does not seem to help
extensions: [extension_path]
)

ActiveSupport.on_load(:active_record) do
database = ActiveRecord::Base.connection.raw_connection
database.enable_load_extension(1)
if Gem.win_platform?
# load DLL from PATH
database.load_extension('liblevenshtein.dll')
else
database.load_extension('/usr/local/lib/liblevenshtein.so.0.0.0')
end
database.enable_load_extension(0)
end

if ENV['LOG_LEVEL'] == 'DEBUG'
begin
warn('Turning on active record logging to active_record.log')
Expand Down
117 changes: 61 additions & 56 deletions lib/ruby_language_server/io.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
# frozen_string_literal: true

require 'json'
require 'socket'

module RubyLanguageServer
class IO
attr_reader :using_socket

def initialize(server, mutex)
@server = server
@mutex = mutex
server.io = self

configure_io

loop do
(id, response) = process_request($stdin)
return_response(id, response, $stdout) unless id.nil?
(id, response) = process_request
return_response(id, response) unless id.nil?
rescue SignalException => e
RubyLanguageServer.logger.error "We received a signal. Let's bail: #{e}"
exit
Expand All @@ -19,38 +25,66 @@ def initialize(server, mutex)
backtrace = e.backtrace * "\n"
RubyLanguageServer.logger.error "Backtrace:\n#{backtrace}"
end
return unless @using_socket

begin
@in&.close
rescue StandardError => e
RubyLanguageServer.logger.error "Error closing socket: #{e}"
end
end

def return_response(id, response, io = $stdout)
def send_notification(message, params)
io ||= out
full_response = {
jsonrpc: '2.0',
id:,
result: response
method: message,
params:
}
response_body = JSON.unparse(full_response)
RubyLanguageServer.logger.info "return_response body: #{response_body}"
io.write "Content-Length: #{response_body.length + 0}\r\n"
body = JSON.generate(full_response)
RubyLanguageServer.logger.info "send_notification body: #{body}"
io.write "Content-Length: #{body.length}\r\n"
io.write "\r\n"
io.write response_body
io.flush
io.write body
io.flush if io.respond_to?(:flush)
end

private

attr_accessor :in, :out

def configure_io
if ENV['LSP_PORT']
@tcp_server = TCPServer.new(ENV['LSP_PORT'].to_i)
RubyLanguageServer.logger.info "Listening on TCP port #{ENV['LSP_PORT']} for LSP connections"
self.in = @tcp_server.accept
self.out = self.in
@using_socket = true
RubyLanguageServer.logger.info 'Accepted LSP socket connection'
else
self.in = $stdin
self.out = $stdout
@using_socket = false
end
end

def send_notification(message, params, io = $stdout)
def return_response(id, response)
io ||= out
full_response = {
jsonrpc: '2.0',
method: message,
params:
id:,
result: response
}
body = JSON.unparse(full_response)
RubyLanguageServer.logger.info "send_notification body: #{body}"
io.write "Content-Length: #{body.length + 0}\r\n"
response_body = JSON.generate(full_response)
RubyLanguageServer.logger.info "return_response body: #{response_body}"
io.write "Content-Length: #{response_body.length}\r\n"
io.write "\r\n"
io.write body
io.flush
io.write response_body
io.flush if io.respond_to?(:flush)
end

def process_request(io = $stdin)
request_body = get_request(io)
def process_request
request_body = get_request
# RubyLanguageServer.logger.debug "request_body: #{request_body}"
request_json = JSON.parse request_body
id = request_json['id']
Expand All @@ -77,14 +111,14 @@ def process_request(io = $stdin)
end
end

def get_request(io = $stdin)
initial_line = get_initial_request_line(io)
def get_request # rubocop:disable Naming/AccessorMethodName
initial_line = get_initial_request_line
RubyLanguageServer.logger.debug "initial_line: #{initial_line}"
length = get_length(initial_line)
content = ''
while content.length < length + 2
begin
content += get_content(length + 2, io) # Why + 2? CRLF?
content += get_content(length + 2) # Why + 2? CRLF?
rescue Exception => e
RubyLanguageServer.logger.error e
# We have almost certainly been disconnected from the server
Expand All @@ -95,8 +129,8 @@ def get_request(io = $stdin)
content
end

def get_initial_request_line(io = $stdin)
io.gets
def get_initial_request_line # rubocop:disable Naming/AccessorMethodName
self.in.gets
end

def get_length(string)
Expand All @@ -105,37 +139,8 @@ def get_length(string)
string.match(/Content-Length: (\d+)/)[1].to_i
end

def get_content(size, io = $stdin)
io.read(size)
def get_content(size)
self.in.read(size)
end

# http://www.alecjacobson.com/weblog/?p=75
# def stdin_read_char
# begin
# # save previous state of stty
# old_state = `stty -g`
# # disable echoing and enable raw (not having to press enter)
# system "stty raw -echo"
# c = STDIN.getc.chr
# # gather next two characters of special keys
# if(c=="\e")
# extra_thread = Thread.new{
# c = c + STDIN.getc.chr
# c = c + STDIN.getc.chr
# }
# # wait just long enough for special keys to get swallowed
# extra_thread.join(0.00001)
# # kill thread so not-so-long special keys don't wait on getc
# extra_thread.kill
# end
# rescue Exception => ex
# puts "#{ex.class}: #{ex.message}"
# puts ex.backtrace
# ensure
# # restore previous state of stty
# system "stty #{old_state}"
# end
# return c
# end
end # class
end # module
Loading
Loading