diff --git a/lib/ruby_lsp/requests/code_lens.rb b/lib/ruby_lsp/requests/code_lens.rb index 5b3681ddbd..d91a29c044 100644 --- a/lib/ruby_lsp/requests/code_lens.rb +++ b/lib/ruby_lsp/requests/code_lens.rb @@ -21,11 +21,11 @@ def provider #: (GlobalState, RubyDocument | ERBDocument, Prism::Dispatcher) -> void def initialize(global_state, document, dispatcher) @response_builder = ResponseBuilders::CollectionResponseBuilder - .new #: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens] + .new(document.encoding, document.parse_result) #: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens] super() @document = document - @test_builder = ResponseBuilders::TestCollection.new #: ResponseBuilders::TestCollection + @test_builder = ResponseBuilders::TestCollection.new(document.encoding, document.parse_result) #: ResponseBuilders::TestCollection uri = document.uri file_path = uri.full_path code_lens_config = global_state.feature_configuration(:codeLens) diff --git a/lib/ruby_lsp/requests/completion.rb b/lib/ruby_lsp/requests/completion.rb index 176c8174a4..38b0376536 100644 --- a/lib/ruby_lsp/requests/completion.rb +++ b/lib/ruby_lsp/requests/completion.rb @@ -61,7 +61,7 @@ def initialize(document, global_state, params, sorbet_level, dispatcher) code_units_cache: document.code_units_cache, ) @response_builder = ResponseBuilders::CollectionResponseBuilder - .new #: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem] + .new(document.encoding, document.parse_result) #: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem] Listeners::Completion.new( @response_builder, diff --git a/lib/ruby_lsp/requests/definition.rb b/lib/ruby_lsp/requests/definition.rb index 53ad3c29ba..97d59e3c99 100644 --- a/lib/ruby_lsp/requests/definition.rb +++ b/lib/ruby_lsp/requests/definition.rb @@ -13,7 +13,7 @@ class Definition < Request def initialize(document, global_state, position, dispatcher, sorbet_level) super() @response_builder = ResponseBuilders::CollectionResponseBuilder - .new #: ResponseBuilders::CollectionResponseBuilder[(Interface::Location | Interface::LocationLink)] + .new(document.encoding, document.parse_result) #: ResponseBuilders::CollectionResponseBuilder[(Interface::Location | Interface::LocationLink)] @dispatcher = dispatcher char_position, _ = document.find_index_by_position(position) diff --git a/lib/ruby_lsp/requests/discover_tests.rb b/lib/ruby_lsp/requests/discover_tests.rb index 527e34928d..212aa6e21c 100644 --- a/lib/ruby_lsp/requests/discover_tests.rb +++ b/lib/ruby_lsp/requests/discover_tests.rb @@ -18,7 +18,7 @@ def initialize(global_state, document, dispatcher) @global_state = global_state @document = document @dispatcher = dispatcher - @response_builder = ResponseBuilders::TestCollection.new #: ResponseBuilders::TestCollection + @response_builder = ResponseBuilders::TestCollection.new(document.encoding, document.parse_result) #: ResponseBuilders::TestCollection end # @override diff --git a/lib/ruby_lsp/requests/document_highlight.rb b/lib/ruby_lsp/requests/document_highlight.rb index 78b1b700ab..1358cae35b 100644 --- a/lib/ruby_lsp/requests/document_highlight.rb +++ b/lib/ruby_lsp/requests/document_highlight.rb @@ -26,7 +26,7 @@ def initialize(global_state, document, position, dispatcher) ) @response_builder = ResponseBuilders::CollectionResponseBuilder - .new #: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight] + .new(document.encoding, document.parse_result) #: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight] Listeners::DocumentHighlight.new( @response_builder, node_context.node, diff --git a/lib/ruby_lsp/requests/document_link.rb b/lib/ruby_lsp/requests/document_link.rb index 0fc72fc17b..e0ed8ded9d 100644 --- a/lib/ruby_lsp/requests/document_link.rb +++ b/lib/ruby_lsp/requests/document_link.rb @@ -16,12 +16,12 @@ def provider end end - #: (URI::Generic uri, Array[Prism::Comment] comments, Prism::Dispatcher dispatcher) -> void - def initialize(uri, comments, dispatcher) + #: (URI::Generic uri, (RubyDocument | ERBDocument) document, Prism::Dispatcher dispatcher) -> void + def initialize(uri, document, dispatcher) super() @response_builder = ResponseBuilders::CollectionResponseBuilder - .new #: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentLink] - Listeners::DocumentLink.new(@response_builder, uri, comments, dispatcher) + .new(document.encoding, document.parse_result) #: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentLink] + Listeners::DocumentLink.new(@response_builder, uri, document.parse_result.comments, dispatcher) end # @override diff --git a/lib/ruby_lsp/requests/document_symbol.rb b/lib/ruby_lsp/requests/document_symbol.rb index ae87fd1aa3..a5672ea350 100644 --- a/lib/ruby_lsp/requests/document_symbol.rb +++ b/lib/ruby_lsp/requests/document_symbol.rb @@ -20,10 +20,10 @@ def provider end end - #: (URI::Generic uri, Prism::Dispatcher dispatcher) -> void - def initialize(uri, dispatcher) + #: (URI::Generic uri, (RubyDocument | ERBDocument) document, Prism::Dispatcher dispatcher) -> void + def initialize(uri, document, dispatcher) super() - @response_builder = ResponseBuilders::DocumentSymbol.new #: ResponseBuilders::DocumentSymbol + @response_builder = ResponseBuilders::DocumentSymbol.new(document.encoding, document.parse_result) #: ResponseBuilders::DocumentSymbol Listeners::DocumentSymbol.new(@response_builder, uri, dispatcher) Addon.addons.each do |addon| diff --git a/lib/ruby_lsp/requests/folding_ranges.rb b/lib/ruby_lsp/requests/folding_ranges.rb index 6880233255..77ba571a6d 100644 --- a/lib/ruby_lsp/requests/folding_ranges.rb +++ b/lib/ruby_lsp/requests/folding_ranges.rb @@ -15,12 +15,12 @@ def provider end end - #: (Array[Prism::Comment] comments, Prism::Dispatcher dispatcher) -> void - def initialize(comments, dispatcher) + #: ((RubyDocument | ERBDocument) document, Prism::Dispatcher dispatcher) -> void + def initialize(document, dispatcher) super() @response_builder = ResponseBuilders::CollectionResponseBuilder - .new #: ResponseBuilders::CollectionResponseBuilder[Interface::FoldingRange] - @listener = Listeners::FoldingRanges.new(@response_builder, comments, dispatcher) #: Listeners::FoldingRanges + .new(document.encoding, document.parse_result) #: ResponseBuilders::CollectionResponseBuilder[Interface::FoldingRange] + @listener = Listeners::FoldingRanges.new(@response_builder, document.parse_result.comments, dispatcher) #: Listeners::FoldingRanges end # @override diff --git a/lib/ruby_lsp/requests/hover.rb b/lib/ruby_lsp/requests/hover.rb index 5095972d2a..0135feca49 100644 --- a/lib/ruby_lsp/requests/hover.rb +++ b/lib/ruby_lsp/requests/hover.rb @@ -46,7 +46,7 @@ def initialize(document, global_state, position, dispatcher, sorbet_level) @target = target #: Prism::Node? uri = document.uri - @response_builder = ResponseBuilders::Hover.new #: ResponseBuilders::Hover + @response_builder = ResponseBuilders::Hover.new(document.encoding, document.parse_result) #: ResponseBuilders::Hover Listeners::Hover.new(@response_builder, global_state, uri, node_context, dispatcher, sorbet_level, position) Addon.addons.each do |addon| addon.create_hover_listener(@response_builder, node_context, dispatcher) diff --git a/lib/ruby_lsp/requests/inlay_hints.rb b/lib/ruby_lsp/requests/inlay_hints.rb index 94d0a03ebe..83fd6100eb 100644 --- a/lib/ruby_lsp/requests/inlay_hints.rb +++ b/lib/ruby_lsp/requests/inlay_hints.rb @@ -21,7 +21,7 @@ def initialize(global_state, document, dispatcher) super() @response_builder = ResponseBuilders::CollectionResponseBuilder - .new #: ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint] + .new(document.encoding, document.parse_result) #: ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint] Listeners::InlayHints.new(global_state, @response_builder, dispatcher) end diff --git a/lib/ruby_lsp/requests/semantic_highlighting.rb b/lib/ruby_lsp/requests/semantic_highlighting.rb index 31a3e05bd4..d558b71ff3 100644 --- a/lib/ruby_lsp/requests/semantic_highlighting.rb +++ b/lib/ruby_lsp/requests/semantic_highlighting.rb @@ -83,7 +83,7 @@ def initialize(global_state, dispatcher, document, previous_result_id, range: ni @range = range @result_id = SemanticHighlighting.next_result_id.to_s #: String @response_builder = ResponseBuilders::SemanticHighlighting - .new(document.code_units_cache) #: ResponseBuilders::SemanticHighlighting + .new(document.encoding, document.parse_result) #: ResponseBuilders::SemanticHighlighting Listeners::SemanticHighlighting.new(dispatcher, @response_builder) Addon.addons.each do |addon| diff --git a/lib/ruby_lsp/requests/signature_help.rb b/lib/ruby_lsp/requests/signature_help.rb index 1a3c11819c..9dfcdea20d 100644 --- a/lib/ruby_lsp/requests/signature_help.rb +++ b/lib/ruby_lsp/requests/signature_help.rb @@ -37,7 +37,7 @@ def initialize(document, global_state, position, context, dispatcher, sorbet_lev @target = target #: Prism::Node? @dispatcher = dispatcher - @response_builder = ResponseBuilders::SignatureHelp.new #: ResponseBuilders::SignatureHelp + @response_builder = ResponseBuilders::SignatureHelp.new(document.encoding, document.parse_result) #: ResponseBuilders::SignatureHelp Listeners::SignatureHelp.new(@response_builder, global_state, node_context, dispatcher, sorbet_level) end diff --git a/lib/ruby_lsp/response_builders/collection_response_builder.rb b/lib/ruby_lsp/response_builders/collection_response_builder.rb index a746dd8d13..fd759797d5 100644 --- a/lib/ruby_lsp/response_builders/collection_response_builder.rb +++ b/lib/ruby_lsp/response_builders/collection_response_builder.rb @@ -5,8 +5,8 @@ module RubyLsp module ResponseBuilders #: [ResponseType < Object] class CollectionResponseBuilder < ResponseBuilder - #: -> void - def initialize + #: (Encoding, Prism::ParseLexResult) -> void + def initialize(encoding, parse_result) super @items = [] #: Array[ResponseType] end diff --git a/lib/ruby_lsp/response_builders/document_symbol.rb b/lib/ruby_lsp/response_builders/document_symbol.rb index 6fea355284..141661888b 100644 --- a/lib/ruby_lsp/response_builders/document_symbol.rb +++ b/lib/ruby_lsp/response_builders/document_symbol.rb @@ -15,8 +15,8 @@ def initialize end end - #: -> void - def initialize + #: (Encoding, Prism::ParseLexResult) -> void + def initialize(encoding, parse_result) super @stack = [SymbolHierarchyRoot.new] #: Array[(SymbolHierarchyRoot | Interface::DocumentSymbol)] end diff --git a/lib/ruby_lsp/response_builders/hover.rb b/lib/ruby_lsp/response_builders/hover.rb index 84dad1ef1d..c632166dc4 100644 --- a/lib/ruby_lsp/response_builders/hover.rb +++ b/lib/ruby_lsp/response_builders/hover.rb @@ -5,8 +5,8 @@ module RubyLsp module ResponseBuilders #: [ResponseType = String] class Hover < ResponseBuilder - #: -> void - def initialize + #: (Encoding, Prism::ParseLexResult) -> void + def initialize(encoding, parse_result) super @response = { diff --git a/lib/ruby_lsp/response_builders/response_builder.rb b/lib/ruby_lsp/response_builders/response_builder.rb index aff52b8609..b8d50555a8 100644 --- a/lib/ruby_lsp/response_builders/response_builder.rb +++ b/lib/ruby_lsp/response_builders/response_builder.rb @@ -5,6 +5,25 @@ module RubyLsp module ResponseBuilders # @abstract class ResponseBuilder + #: (Encoding, Prism::ParseLexResult) -> void + def initialize(encoding, parse_result) + @encoding = encoding + @code_units_cache = parse_result.code_units_cache(encoding) #: (^(Integer arg0) -> Integer | Prism::CodeUnitsCache) + end + + #: (Prism::Location) -> Interface::Range + def range_from_location(location) + Interface::Range.new( + start: Interface::Position.new(line: location.start_line - 1, character: location.cached_start_code_units_column(@code_units_cache)), + end: Interface::Position.new(line: location.end_line - 1, character: location.cached_end_code_units_column(@code_units_cache)), + ) + end + + #: (Prism::Node) -> Interface::Range + def range_from_node(node) + range_from_location(node.location) + end + # @abstract #: -> top def response diff --git a/lib/ruby_lsp/response_builders/semantic_highlighting.rb b/lib/ruby_lsp/response_builders/semantic_highlighting.rb index f797e28460..51208ecccd 100644 --- a/lib/ruby_lsp/response_builders/semantic_highlighting.rb +++ b/lib/ruby_lsp/response_builders/semantic_highlighting.rb @@ -46,10 +46,10 @@ class UndefinedTokenType < StandardError; end defaultLibrary: 9, }.freeze #: Hash[Symbol, Integer] - #: ((^(Integer arg0) -> Integer | Prism::CodeUnitsCache) code_units_cache) -> void - def initialize(code_units_cache) - super() - @code_units_cache = code_units_cache + #: (Encoding, Prism::ParseLexResult) -> void + def initialize(encoding, parse_result) + super + @code_units_cache = parse_result.code_units_cache(encoding) #: (^(Integer arg0) -> Integer | Prism::CodeUnitsCache) @stack = [] #: Array[SemanticToken] end diff --git a/lib/ruby_lsp/response_builders/signature_help.rb b/lib/ruby_lsp/response_builders/signature_help.rb index 780f286725..bf59d5feae 100644 --- a/lib/ruby_lsp/response_builders/signature_help.rb +++ b/lib/ruby_lsp/response_builders/signature_help.rb @@ -5,8 +5,8 @@ module RubyLsp module ResponseBuilders #: [ResponseType = Interface::SignatureHelp?] class SignatureHelp < ResponseBuilder - #: -> void - def initialize + #: (Encoding, Prism::ParseLexResult) -> void + def initialize(encoding, parse_result) super @signature_help = nil #: ResponseType end diff --git a/lib/ruby_lsp/response_builders/test_collection.rb b/lib/ruby_lsp/response_builders/test_collection.rb index 70f9a3957a..9343399f2c 100644 --- a/lib/ruby_lsp/response_builders/test_collection.rb +++ b/lib/ruby_lsp/response_builders/test_collection.rb @@ -8,8 +8,8 @@ class TestCollection < ResponseBuilder #: Array[Interface::CodeLens] attr_reader :code_lens - #: -> void - def initialize + #: (Encoding, Prism::ParseLexResult) -> void + def initialize(encoding, parse_result) super @items = {} #: Hash[String, ResponseType] @code_lens = [] #: Array[Interface::CodeLens] diff --git a/lib/ruby_lsp/server.rb b/lib/ruby_lsp/server.rb index e2dc3ab1c2..64172b6f8e 100644 --- a/lib/ruby_lsp/server.rb +++ b/lib/ruby_lsp/server.rb @@ -491,9 +491,9 @@ def run_combined_requests(message) # Run requests for the document dispatcher = Prism::Dispatcher.new - folding_range = Requests::FoldingRanges.new(parse_result.comments, dispatcher) - document_symbol = Requests::DocumentSymbol.new(uri, dispatcher) - document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher) + folding_range = Requests::FoldingRanges.new(document, dispatcher) + document_symbol = Requests::DocumentSymbol.new(uri, document, dispatcher) + document_link = Requests::DocumentLink.new(uri, document, dispatcher) inlay_hint = Requests::InlayHints.new( @global_state, document, diff --git a/test/requests/document_link_expectations_test.rb b/test/requests/document_link_expectations_test.rb index 6893421735..df4823e061 100644 --- a/test/requests/document_link_expectations_test.rb +++ b/test/requests/document_link_expectations_test.rb @@ -25,8 +25,7 @@ def run_expectations(source) document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: uri, global_state: @global_state) dispatcher = Prism::Dispatcher.new - parse_result = document.parse_result - listener = RubyLsp::Requests::DocumentLink.new(uri, parse_result.comments, dispatcher) + listener = RubyLsp::Requests::DocumentLink.new(uri, document, dispatcher) dispatcher.dispatch(document.ast) listener.perform end diff --git a/test/requests/document_symbol_expectations_test.rb b/test/requests/document_symbol_expectations_test.rb index ab5bedd978..3d77986052 100644 --- a/test/requests/document_symbol_expectations_test.rb +++ b/test/requests/document_symbol_expectations_test.rb @@ -20,7 +20,7 @@ def test_instance_variable_with_shorthand_assignment document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: uri, global_state: @global_state) dispatcher = Prism::Dispatcher.new - listener = RubyLsp::Requests::DocumentSymbol.new(uri, dispatcher) + listener = RubyLsp::Requests::DocumentSymbol.new(uri, document, dispatcher) dispatcher.dispatch(document.ast) response = listener.perform @@ -43,7 +43,7 @@ def test_instance_variable_with_destructuring_assignment document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: uri, global_state: @global_state) dispatcher = Prism::Dispatcher.new - listener = RubyLsp::Requests::DocumentSymbol.new(uri, dispatcher) + listener = RubyLsp::Requests::DocumentSymbol.new(uri, document, dispatcher) dispatcher.dispatch(document.ast) response = listener.perform @@ -65,7 +65,7 @@ def test_labels_blank_names document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: uri, global_state: @global_state) dispatcher = Prism::Dispatcher.new - listener = RubyLsp::Requests::DocumentSymbol.new(uri, dispatcher) + listener = RubyLsp::Requests::DocumentSymbol.new(uri, document, dispatcher) dispatcher.dispatch(document.ast) response = listener.perform @@ -114,7 +114,7 @@ def run_expectations(source) document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: uri, global_state: @global_state) dispatcher = Prism::Dispatcher.new - listener = RubyLsp::Requests::DocumentSymbol.new(uri, dispatcher) + listener = RubyLsp::Requests::DocumentSymbol.new(uri, document, dispatcher) dispatcher.dispatch(document.ast) listener.perform end diff --git a/test/requests/folding_ranges_expectations_test.rb b/test/requests/folding_ranges_expectations_test.rb index 6f0cbba6c8..accde7ac1e 100644 --- a/test/requests/folding_ranges_expectations_test.rb +++ b/test/requests/folding_ranges_expectations_test.rb @@ -12,8 +12,7 @@ def run_expectations(source) document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: uri, global_state: @global_state) dispatcher = Prism::Dispatcher.new - parse_result = document.parse_result - listener = RubyLsp::Requests::FoldingRanges.new(parse_result.comments, dispatcher) + listener = RubyLsp::Requests::FoldingRanges.new(document, dispatcher) dispatcher.dispatch(document.ast) listener.perform end diff --git a/test/response_builders/collection_response_builder_test.rb b/test/response_builders/collection_response_builder_test.rb new file mode 100644 index 0000000000..cac08c87fb --- /dev/null +++ b/test/response_builders/collection_response_builder_test.rb @@ -0,0 +1,50 @@ +# typed: true +# frozen_string_literal: true + +require "test_helper" + +module RubyLsp + class CollectionResponseBuilderTest < Minitest::Test + def test_range_from_location_respects_negotiated_position_encoding + # Offsets for `foo` on the single line "🌍café"; foo: + # " 🌍 c a f é " ; _ | foo + # bytes 1 4 1 1 1 2 1 1 1 | 13..16 + # utf16 1 2 1 1 1 1 1 1 1 | 10..13 + # utf32 1 1 1 1 1 1 1 1 1 | 9..12 + source = "\"🌍café\"; foo" + parse_result = Prism.parse_lex(source) + location = parse_result.value[0].statements.body.last.location + + # UTF-8: number of bytes + assert_range( + ResponseBuilders::CollectionResponseBuilder.new(Encoding::UTF_8, parse_result).range_from_location(location), + start_character: 13, + end_character: 16, + ) + + # UTF-16: number of UTF-16 code units (length 1 for 1/2 byte characters, length 2 for 3/4 byte characters) + assert_range( + ResponseBuilders::CollectionResponseBuilder.new(Encoding::UTF_16LE, parse_result).range_from_location(location), + start_character: 10, + end_character: 13, + ) + + # UTF-32: number of UTF-32 code points (length 1 for all characters) + assert_range( + ResponseBuilders::CollectionResponseBuilder.new(Encoding::UTF_32LE, parse_result).range_from_location(location), + start_character: 9, + end_character: 12, + ) + end + + private + + #: (Interface::Range, start_character: Integer, end_character: Integer) -> void + def assert_range(range, start_character:, end_character:) + assert_equal(0, range.start.line) + assert_equal(0, range.end.line) + assert_equal(start_character, range.start.character) + assert_equal(end_character, range.end.character) + end + end +end diff --git a/test/response_builders/test_collection_test.rb b/test/response_builders/test_collection_test.rb index 74f60f0625..c58eb46dd7 100644 --- a/test/response_builders/test_collection_test.rb +++ b/test/response_builders/test_collection_test.rb @@ -11,10 +11,11 @@ def setup start: Interface::Position.new(line: 0, character: 0), end: Interface::Position.new(line: 10, character: 3), ) + @parse_result = Prism.parse_lex("") end def test_allows_building_hierarchy_of_tests - builder = ResponseBuilders::TestCollection.new + builder = ResponseBuilders::TestCollection.new(Encoding::UTF_8, @parse_result) test_item = Requests::Support::TestItem.new("my-id", "Test label", @uri, @range, framework: :minitest) nested_item = Requests::Support::TestItem.new("nested-id", "Nested label", @uri, @range, framework: :minitest) @@ -29,7 +30,7 @@ def test_allows_building_hierarchy_of_tests end def test_overrides_if_trying_to_add_item_with_same_id - builder = ResponseBuilders::TestCollection.new + builder = ResponseBuilders::TestCollection.new(Encoding::UTF_8, @parse_result) test_item = Requests::Support::TestItem.new("my-id", "Test label", @uri, @range, framework: :minitest) nested_item = Requests::Support::TestItem.new("nested-id", "Nested label", @uri, @range, framework: :minitest)