From 112b70b4afbd43899d84eaf8925d3840847dfe2f Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 14 Aug 2024 14:01:06 +0900 Subject: [PATCH 1/5] Type check --- lib/steep/server/master.rb | 2 +- lib/steep/server/type_check_worker.rb | 4 +--- sig/steep/server/master.rbs | 2 +- sig/steep/server/type_check_worker.rbs | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/steep/server/master.rb b/lib/steep/server/master.rb index a62ec4620..2e2a0a2b7 100644 --- a/lib/steep/server/master.rb +++ b/lib/steep/server/master.rb @@ -380,7 +380,7 @@ def response? include MessageUtils end - SendMessageJob = _ = Struct.new(:dest, :message, keyword_init: true) do + class SendMessageJob < Struct.new(:dest, :message, keyword_init: true) # @implements SendMessageJob def self.to_worker(worker, message:) diff --git a/lib/steep/server/type_check_worker.rb b/lib/steep/server/type_check_worker.rb index 92cce5ee2..5775461d8 100644 --- a/lib/steep/server/type_check_worker.rb +++ b/lib/steep/server/type_check_worker.rb @@ -11,9 +11,7 @@ class TypeCheckWorker < BaseWorker TypeCheckCodeJob = _ = Struct.new(:guid, :path, keyword_init: true) ValidateAppSignatureJob = _ = Struct.new(:guid, :path, keyword_init: true) ValidateLibrarySignatureJob = _ = Struct.new(:guid, :path, keyword_init: true) - GotoJob = _ = Struct.new(:id, :kind, :params, keyword_init: true) do - # @implements GotoJob - + class GotoJob < Struct.new(:id, :kind, :params, keyword_init: true) def self.implementation(id:, params:) new( kind: :implementation, diff --git a/sig/steep/server/master.rbs b/sig/steep/server/master.rbs index bc2e1c263..e40530496 100644 --- a/sig/steep/server/master.rbs +++ b/sig/steep/server/master.rbs @@ -213,7 +213,7 @@ module Steep attr_reader message: untyped - def initialize: (dest: WorkerProcess | :client, message: untyped) -> void + def self.new: (dest: WorkerProcess | :client, message: untyped) -> instance def self.to_worker: (WorkerProcess, message: untyped) -> SendMessageJob diff --git a/sig/steep/server/type_check_worker.rbs b/sig/steep/server/type_check_worker.rbs index 88537baf0..6c4a09033 100644 --- a/sig/steep/server/type_check_worker.rbs +++ b/sig/steep/server/type_check_worker.rbs @@ -82,7 +82,7 @@ module Steep attr_reader params: params - def initialize: (id: String, params: params, kind: kind) -> void + def self.new: (id: String, params: params, kind: kind) -> instance def self.implementation: (id: String, params: params) -> GotoJob From 3437aa5c023d7eaab9bfa94f065715ccfe69ae17 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 14 Aug 2024 14:05:37 +0900 Subject: [PATCH 2/5] Load files in master process --- lib/steep/server/change_buffer.rb | 11 +++-------- lib/steep/server/interaction_worker.rb | 7 +++++-- lib/steep/server/master.rb | 22 ++++++++++++++++++++-- lib/steep/server/type_check_worker.rb | 5 ++++- sig/steep/server/change_buffer.rbs | 2 +- sig/steep/server/master.rbs | 2 +- 6 files changed, 34 insertions(+), 15 deletions(-) diff --git a/lib/steep/server/change_buffer.rb b/lib/steep/server/change_buffer.rb index fe64bd098..d9079b333 100644 --- a/lib/steep/server/change_buffer.rb +++ b/lib/steep/server/change_buffer.rb @@ -24,16 +24,11 @@ def pop_buffer end end - def load_files(project:, commandline_args:) + def load_files(input) Steep.logger.tagged "#load_files" do push_buffer do |changes| - loader = Services::FileLoader.new(base_dir: project.base_dir) - - Steep.measure "load changes from disk" do - project.targets.each do |target| - loader.load_changes(target.source_pattern, commandline_args, changes: changes) - loader.load_changes(target.signature_pattern, changes: changes) - end + input.each do |filename, content| + changes[Pathname(filename.to_s)] = [Services::ContentChange.new(text: content)] end end end diff --git a/lib/steep/server/interaction_worker.rb b/lib/steep/server/interaction_worker.rb index d624e1781..9745957fb 100644 --- a/lib/steep/server/interaction_worker.rb +++ b/lib/steep/server/interaction_worker.rb @@ -82,14 +82,17 @@ def queue_job(job) def handle_request(request) case request[:method] when "initialize" - load_files(project: project, commandline_args: []) - queue_job ApplyChangeJob.new writer.write({ id: request[:id], result: nil }) when "textDocument/didChange" collect_changes(request) queue_job ApplyChangeJob.new + when "$/file/load" + input = request[:params][:content] + load_files(input) + queue_job ApplyChangeJob.new + when "$/file/reset" uri = request[:params][:uri] text = request[:params][:content] diff --git a/lib/steep/server/master.rb b/lib/steep/server/master.rb index 2e2a0a2b7..41bd5a6fa 100644 --- a/lib/steep/server/master.rb +++ b/lib/steep/server/master.rb @@ -150,6 +150,8 @@ def initialize(project:) def load(command_line_args:) loader = Services::FileLoader.new(base_dir: project.base_dir) + files = {} #: Hash[String, String] + target_paths.each do |paths| target = paths.target @@ -158,13 +160,25 @@ def load(command_line_args:) loader.each_path_in_patterns(target.source_pattern, command_line_args) do |path| paths.code_paths << project.absolute_path(path) + files[path.to_s] = project.absolute_path(path).read + if files.size > 1000 + yield files.dup + files.clear + end end loader.each_path_in_patterns(target.signature_pattern) do |path| paths.signature_paths << project.absolute_path(path) + files[path.to_s] = project.absolute_path(path).read + if files.size > 1000 + yield files.dup + files.clear + end end changed_paths.merge(paths.all_paths) end + + yield files.dup unless files.empty? end def push_changes(path) @@ -556,9 +570,13 @@ def process_message_from_client(message) group << send_request(method: "initialize", params: message[:params], worker: worker) end - group.on_completion do - controller.load(command_line_args: commandline_args) + Steep.measure("Load files from disk...") do + controller.load(command_line_args: commandline_args) do |input| + broadcast_notification({ method: "$/file/load", params: { content: input } }) + end + end + group.on_completion do job_queue << SendMessageJob.to_client( message: { id: id, diff --git a/lib/steep/server/type_check_worker.rb b/lib/steep/server/type_check_worker.rb index 5775461d8..6819e7004 100644 --- a/lib/steep/server/type_check_worker.rb +++ b/lib/steep/server/type_check_worker.rb @@ -66,12 +66,15 @@ def initialize(project:, reader:, writer:, assignment:, commandline_args:) def handle_request(request) case request[:method] when "initialize" - load_files(project: project, commandline_args: commandline_args) writer.write({ id: request[:id], result: nil}) when "textDocument/didChange" collect_changes(request) + when "$/file/load" + input = request[:params][:content] + load_files(input) + when "$/file/reset" uri = request[:params][:uri] text = request[:params][:content] diff --git a/sig/steep/server/change_buffer.rbs b/sig/steep/server/change_buffer.rbs index b6a061131..9860aac28 100644 --- a/sig/steep/server/change_buffer.rbs +++ b/sig/steep/server/change_buffer.rbs @@ -24,7 +24,7 @@ module Steep # Load files from `project` to `buffered_changes` # - def load_files: (project: Project, commandline_args: Array[String]) -> void + def load_files: (Hash[String, String] input) -> void # Load changes from a request with `DidChangeTextDocumentParams` into `buffered_changes` # diff --git a/sig/steep/server/master.rbs b/sig/steep/server/master.rbs index e40530496..5543dad41 100644 --- a/sig/steep/server/master.rbs +++ b/sig/steep/server/master.rbs @@ -110,7 +110,7 @@ module Steep def initialize: (project: Project) -> void - def load: (command_line_args: Array[String]) -> void + def load: (command_line_args: Array[String]) { (Hash[String, String]) -> void } -> void def push_changes: (Pathname path) -> void From 8ad07cb9e5e3d324d33fb2d3bb91737c8902e285 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Thu, 15 Aug 2024 12:50:11 +0900 Subject: [PATCH 3/5] Handle non-utf8 files --- lib/steep.rb | 1 + lib/steep/server/change_buffer.rb | 3 +++ lib/steep/server/master.rb | 8 ++++++++ rbs_collection.steep.yaml | 1 + sig/steep/server/change_buffer.rbs | 4 +++- sig/steep/server/master.rbs | 2 +- 6 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/steep.rb b/lib/steep.rb index 4710cbc1f..a1aa4afde 100644 --- a/lib/steep.rb +++ b/lib/steep.rb @@ -15,6 +15,7 @@ require 'uri' require "yaml" require "securerandom" +require "base64" require "concurrent/utility/processor_counter" require "terminal-table" diff --git a/lib/steep/server/change_buffer.rb b/lib/steep/server/change_buffer.rb index d9079b333..6a9d002a6 100644 --- a/lib/steep/server/change_buffer.rb +++ b/lib/steep/server/change_buffer.rb @@ -28,6 +28,9 @@ def load_files(input) Steep.logger.tagged "#load_files" do push_buffer do |changes| input.each do |filename, content| + if content.is_a?(Hash) + content = Base64.decode64(content[:text]).force_encoding(Encoding::UTF_8) + end changes[Pathname(filename.to_s)] = [Services::ContentChange.new(text: content)] end end diff --git a/lib/steep/server/master.rb b/lib/steep/server/master.rb index 41bd5a6fa..67bb32491 100644 --- a/lib/steep/server/master.rb +++ b/lib/steep/server/master.rb @@ -572,6 +572,14 @@ def process_message_from_client(message) Steep.measure("Load files from disk...") do controller.load(command_line_args: commandline_args) do |input| + input.transform_values! do |content| + content.is_a?(String) or raise + if content.valid_encoding? + content + else + { text: Base64.encode64(content), binary: true } + end + end broadcast_notification({ method: "$/file/load", params: { content: input } }) end end diff --git a/rbs_collection.steep.yaml b/rbs_collection.steep.yaml index 54f164181..df401c45a 100644 --- a/rbs_collection.steep.yaml +++ b/rbs_collection.steep.yaml @@ -23,3 +23,4 @@ gems: - name: securerandom - name: ffi ignore: true + - name: base64 diff --git a/sig/steep/server/change_buffer.rbs b/sig/steep/server/change_buffer.rbs index 9860aac28..4d6e33bc4 100644 --- a/sig/steep/server/change_buffer.rbs +++ b/sig/steep/server/change_buffer.rbs @@ -22,9 +22,11 @@ module Steep def pop_buffer: [A] () { (changes) -> A } -> A | () -> changes + type content = String | { text: String, binary: true } + # Load files from `project` to `buffered_changes` # - def load_files: (Hash[String, String] input) -> void + def load_files: (Hash[String, content] input) -> void # Load changes from a request with `DidChangeTextDocumentParams` into `buffered_changes` # diff --git a/sig/steep/server/master.rbs b/sig/steep/server/master.rbs index 5543dad41..c98f51ab4 100644 --- a/sig/steep/server/master.rbs +++ b/sig/steep/server/master.rbs @@ -110,7 +110,7 @@ module Steep def initialize: (project: Project) -> void - def load: (command_line_args: Array[String]) { (Hash[String, String]) -> void } -> void + def load: (command_line_args: Array[String]) { (Hash[String, ChangeBuffer::content]) -> void } -> void def push_changes: (Pathname path) -> void From 427fe4bba4bb29b572fbafed4bf7373eef13caaf Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Thu, 15 Aug 2024 10:50:32 +0900 Subject: [PATCH 4/5] Fix logger setup --- lib/steep.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/steep.rb b/lib/steep.rb index a1aa4afde..0e992c360 100644 --- a/lib/steep.rb +++ b/lib/steep.rb @@ -161,7 +161,11 @@ def self.ui_logger end def self.new_logger(output, prev_level) - ActiveSupport::TaggedLogging.new(Logger.new(output)).tap do |logger| + logger = Logger.new(output) + logger.formatter = proc do |severity, datetime, progname, msg| + "#{datetime.strftime('%Y-%m-%d %H:%M:%S')}: #{severity}: #{msg}\n" + end + ActiveSupport::TaggedLogging.new(logger).tap do |logger| logger.push_tags "Steep #{VERSION}" logger.level = prev_level || Logger::ERROR end From fc04083b609a148c11e7672ff8a45ceecd251a71 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Thu, 15 Aug 2024 12:50:15 +0900 Subject: [PATCH 5/5] Fix tests --- test/interaction_worker_test.rb | 1 + test/master_type_check_controller_test.rb | 14 ++++----- test/type_check_worker_test.rb | 38 ----------------------- 3 files changed, 8 insertions(+), 45 deletions(-) diff --git a/test/interaction_worker_test.rb b/test/interaction_worker_test.rb index 3ef0d2e40..1f7230c72 100644 --- a/test/interaction_worker_test.rb +++ b/test/interaction_worker_test.rb @@ -53,6 +53,7 @@ def test_handle_request_initialize worker = InteractionWorker.new(project: project, reader: worker_reader, writer: worker_writer) worker.handle_request({ method: "initialize", id: 1, params: nil }) + worker.handle_request({ method: "$/file/load", params: { content: {} } }) q = flush_queue(worker.queue) assert_equal 1, q.size diff --git a/test/master_type_check_controller_test.rb b/test/master_type_check_controller_test.rb index 42e848b72..f7ebdf369 100644 --- a/test/master_type_check_controller_test.rb +++ b/test/master_type_check_controller_test.rb @@ -104,7 +104,7 @@ class Customer Project::DSL.parse(project, steepfile.read) controller = Server::Master::TypeCheckController.new(project: project) - controller.load(command_line_args: []) + controller.load(command_line_args: []) {} controller.target_paths[0].tap do |paths| assert_equal Set[current_dir + "lib/customer.rb"], paths.code_paths @@ -137,7 +137,7 @@ class Customer Project::DSL.parse(project, steepfile.read) controller = Server::Master::TypeCheckController.new(project: project) - controller.load(command_line_args: []) + controller.load(command_line_args: []) {} controller.changed_paths.clear() controller.push_changes(current_dir + "lib/customer.rb") @@ -177,7 +177,7 @@ class Customer Project::DSL.parse(project, steepfile.read) controller = Server::Master::TypeCheckController.new(project: project) - controller.load(command_line_args: []) + controller.load(command_line_args: []) {} controller.changed_paths.clear() controller.update_priority(open: current_dir + "lib/customer.rb") @@ -213,7 +213,7 @@ class Customer Project::DSL.parse(project, steepfile.read) controller = Server::Master::TypeCheckController.new(project: project) - controller.load(command_line_args: []) + controller.load(command_line_args: []) {} controller.changed_paths.clear() assert_nil controller.make_request() @@ -241,7 +241,7 @@ class Customer Project::DSL.parse(project, steepfile.read) controller = Server::Master::TypeCheckController.new(project: project) - controller.load(command_line_args: []) + controller.load(command_line_args: []) {} controller.changed_paths.clear() controller.update_priority(open: current_dir + "lib/customer.rb") @@ -278,7 +278,7 @@ class Customer Project::DSL.parse(project, steepfile.read) controller = Server::Master::TypeCheckController.new(project: project) - controller.load(command_line_args: []) + controller.load(command_line_args: []) {} controller.changed_paths.clear() controller.update_priority(open: current_dir + "lib/customer.rb") @@ -319,7 +319,7 @@ class Account Project::DSL.parse(project, steepfile.read) controller = Server::Master::TypeCheckController.new(project: project) - controller.load(command_line_args: []) + controller.load(command_line_args: []) {} controller.changed_paths.clear() controller.update_priority(open: current_dir + "lib/customer.rb") diff --git a/test/type_check_worker_test.rb b/test/type_check_worker_test.rb index b667dd60d..5d56eae8b 100644 --- a/test/type_check_worker_test.rb +++ b/test/type_check_worker_test.rb @@ -186,7 +186,6 @@ def test_handle_request_typecheck_start reader: worker_reader, writer: worker_writer ) - worker.load_files(project: worker.project, commandline_args: []) worker.handle_request( { @@ -250,7 +249,6 @@ def test_handle_job_start_typecheck reader: worker_reader, writer: worker_writer ) - worker.load_files(project: worker.project, commandline_args: []) changes = {} changes[Pathname("lib/hello.rb")] = [Services::ContentChange.string(<<~RUBY)] @@ -298,7 +296,6 @@ def test_handle_job_validate_app_signature writer: worker_writer ) - worker.load_files(project: worker.project, commandline_args: []) worker.instance_variable_set(:@current_type_check_guid, "guid") {}.tap do |changes| @@ -349,7 +346,6 @@ def test_handle_job_validate_app_signature_skip writer: worker_writer ) - worker.load_files(project: worker.project, commandline_args: []) worker.instance_variable_set(:@current_type_check_guid, nil) {}.tap do |changes| @@ -396,7 +392,6 @@ def test_handle_job_validate_lib_signature writer: worker_writer ) - worker.load_files(project: worker.project, commandline_args: []) worker.instance_variable_set(:@current_type_check_guid, "guid") {}.tap do |changes| @@ -450,7 +445,6 @@ def test_handle_job_validate_lib_signature_skip writer: worker_writer ) - worker.load_files(project: worker.project, commandline_args: []) worker.instance_variable_set(:@current_type_check_guid, nil) {}.tap do |changes| @@ -498,7 +492,6 @@ def test_handle_job_typecheck_code writer: worker_writer ) - worker.load_files(project: worker.project, commandline_args: []) worker.instance_variable_set(:@current_type_check_guid, "guid") {}.tap do |changes| @@ -557,7 +550,6 @@ def test_handle_job_typecheck_code_diagnostics writer: worker_writer ) - worker.load_files(project: worker.project, commandline_args: []) worker.instance_variable_set(:@current_type_check_guid, "guid") {}.tap do |changes| @@ -619,7 +611,6 @@ def test_handle_job_typecheck_skip writer: worker_writer ) - worker.load_files(project: worker.project, commandline_args: []) worker.instance_variable_set(:@current_type_check_guid, nil) {}.tap do |changes| @@ -685,35 +676,6 @@ def new_class_method: () -> void end end - def test_loading_files_with_args - in_tmpdir do - project = Project.new(steepfile_path: current_dir + "Steepfile") - Project::DSL.parse(project, <