diff --git a/lib/steep/server/interaction_worker.rb b/lib/steep/server/interaction_worker.rb index 9745957f..f3c7a864 100644 --- a/lib/steep/server/interaction_worker.rb +++ b/lib/steep/server/interaction_worker.rb @@ -6,6 +6,7 @@ class InteractionWorker < BaseWorker ApplyChangeJob = _ = Class.new() HoverJob = _ = Struct.new(:id, :path, :line, :column, keyword_init: true) CompletionJob = _ = Struct.new(:id, :path, :line, :column, :trigger, keyword_init: true) + FormattingJob = _ = Struct.new(:id, :path, keyword_init: true) SignatureHelpJob = _ = Struct.new(:id, :path, :line, :column, keyword_init: true) LSP = LanguageServer::Protocol @@ -54,6 +55,13 @@ def handle_job(job) result: process_latest_job(job) { process_signature_help(job) } } ) + when FormattingJob + writer.write( + { + id: job.id, + result: process_latest_job(job) { process_formatting(job) } + } + ) end end end @@ -125,6 +133,12 @@ def handle_request(request) line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] } queue_job SignatureHelpJob.new(id: id, path: path, line: line, column: column) + when "textDocument/formatting" + id = request[:id] + params = request[:params] + path = project.relative_path(PathHelper.to_pathname!(params[:textDocument][:uri])) + + queue_job FormattingJob.new(id: id, path: path) end end @@ -494,6 +508,41 @@ def process_signature_help(job) # Reuse the latest result to keep SignatureHelp opened while typing @last_signature_help_result if @last_signature_help_line == job.line end + + def process_formatting(job) + Steep.logger.tagged("#response_to_formatting") do + Steep.measure "Generating response" do + if (targets = project.targets_for_path(job.path)).is_a?(Array) + target = targets[0] or raise + sig_service = service.signature_services[target.name] or raise + file = sig_service.files[job.path] + + parsed = RBS::Parser.parse_signature(file.content) + new_text = StringIO.new.tap do |out| + RBS::Writer.new(out: out).write(parsed[1] + parsed[2]) + end.string + + [ + LSP::Interface::TextEdit.new( + range: LSP::Interface::Range.new( + start: LSP::Interface::Position.new( + line: 0, + character: 0 + ), + end: LSP::Interface::Position.new( + line: file.content.count("\n") + 1, + character: 0 + ) + ), + new_text: new_text + ) + ] + else + nil + end + end + end + end end end end diff --git a/lib/steep/server/master.rb b/lib/steep/server/master.rb index 67bb3249..8bf8d134 100644 --- a/lib/steep/server/master.rb +++ b/lib/steep/server/master.rb @@ -610,7 +610,8 @@ def process_message_from_client(message) definition_provider: true, declaration_provider: false, implementation_provider: true, - type_definition_provider: true + type_definition_provider: true, + document_formatting_provider: true ) ) } @@ -828,6 +829,29 @@ def process_message_from_client(message) ) end + when "textDocument/formatting" + if interaction_worker + if message[:params][:textDocument][:uri].to_s =~ /\.rbs$/i + result_controller << send_request(method: message[:method], params: message[:params], worker: interaction_worker) do |handler| + handler.on_completion do |response| + job_queue << SendMessageJob.to_client( + message: { + id: message[:id], + result: response[:result] + } + ) + end + end + else + job_queue << SendMessageJob.to_client( + message: { + id: message[:id], + result: nil + } + ) + end + end + when "$/typecheck" request = controller.make_request( guid: message[:params][:guid], diff --git a/sig/steep/server/interaction_worker.rbs b/sig/steep/server/interaction_worker.rbs index 908977a2..e50e6ebf 100644 --- a/sig/steep/server/interaction_worker.rbs +++ b/sig/steep/server/interaction_worker.rbs @@ -36,6 +36,14 @@ module Steep def initialize: (id: String, path: Pathname, line: Integer, column: Integer, trigger: String) -> void end + class FormattingJob + attr_reader id: String + + attr_reader path: Pathname + + def initialize: (id: String, path: Pathname) -> void + end + class SignatureHelpJob attr_reader id: String @@ -48,7 +56,7 @@ module Steep def initialize: (id: String, path: Pathname, line: Integer, column: Integer) -> void end - type job = ApplyChangeJob | HoverJob | CompletionJob | SignatureHelpJob + type job = ApplyChangeJob | HoverJob | CompletionJob | FormattingJob | SignatureHelpJob module LSP = LanguageServer::Protocol @@ -85,6 +93,8 @@ module Steep def format_completion_item_for_rbs: (Services::SignatureService, RBS::TypeName, CompletionJob job, String complete_text, Integer prefix_size) -> LanguageServer::Protocol::Interface::CompletionItem def format_completion_item: (CompletionProvider::item item) -> LanguageServer::Protocol::Interface::CompletionItem + + def process_formatting: (FormattingJob job) -> Array[LanguageServer::Protocol::Interface::TextEdit]? end end end diff --git a/test/interaction_worker_test.rb b/test/interaction_worker_test.rb index 1f7230c7..f1c76443 100644 --- a/test/interaction_worker_test.rb +++ b/test/interaction_worker_test.rb @@ -442,5 +442,37 @@ class Qux end end end + + def test_handle_formatting_request + in_tmpdir do + project = Project.new(steepfile_path: current_dir + "Steepfile") + Project::DSL.parse(project, < [ContentChange.string(< String | () -> Integer + end +RUBY + } + ) {} + response = worker.process_formatting( + InteractionWorker::FormattingJob.new( + path: Pathname("sig/hello.rbs") + ) + ) + + assert_instance_of Array, response + assert_equal 1, response.size + assert_instance_of LanguageServer::Protocol::Interface::TextEdit, response[0] + end + end +end