File tree Expand file tree Collapse file tree 4 files changed +76
-2
lines changed Expand file tree Collapse file tree 4 files changed +76
-2
lines changed Original file line number Diff line number Diff line change @@ -15,6 +15,7 @@ class LanguageId < T::Enum
1515 extend T ::Helpers
1616 extend T ::Generic
1717
18+ class LocationNotFoundError < StandardError ; end
1819 ParseResultType = type_member
1920
2021 # This maximum number of characters for providing expensive features, like semantic highlighting and diagnostics.
@@ -144,7 +145,15 @@ def initialize(source, encoding)
144145 def find_char_position ( position )
145146 # Find the character index for the beginning of the requested line
146147 until @current_line == position [ :line ]
147- @pos += 1 until LINE_BREAK == @source [ @pos ]
148+ until LINE_BREAK == @source [ @pos ]
149+ @pos += 1
150+
151+ if @pos >= @source . length
152+ # Pack the code points back into the original string to provide context in the error message
153+ raise LocationNotFoundError , "Requested position: #{ position } \n Source:\n \n #{ @source . pack ( "U*" ) } "
154+ end
155+ end
156+
148157 @pos += 1
149158 @current_line += 1
150159 end
Original file line number Diff line number Diff line change @@ -119,12 +119,24 @@ def process_message(message)
119119 # If a document is deleted before we are able to process all of its enqueued requests, we will try to read it
120120 # from disk and it raise this error. This is expected, so we don't include the `data` attribute to avoid
121121 # reporting these to our telemetry
122- if e . is_a? ( Store ::NonExistingDocumentError )
122+ case e
123+ when Store ::NonExistingDocumentError
123124 send_message ( Error . new (
124125 id : message [ :id ] ,
125126 code : Constant ::ErrorCodes ::INVALID_PARAMS ,
126127 message : e . full_message ,
127128 ) )
129+ when Document ::LocationNotFoundError
130+ send_message ( Error . new (
131+ id : message [ :id ] ,
132+ code : Constant ::ErrorCodes ::REQUEST_FAILED ,
133+ message : <<~MESSAGE ,
134+ Request #{ message [ :method ] } failed to find the target position.
135+ The file might have been modified while the server was in the middle of searching for the target.
136+ If you experience this regularly, please report any findings and extra information on
137+ https://github.com/Shopify/ruby-lsp/issues/2446
138+ MESSAGE
139+ ) )
128140 else
129141 send_message ( Error . new (
130142 id : message [ :id ] ,
Original file line number Diff line number Diff line change @@ -770,6 +770,29 @@ class Foo
770770 assert_nil ( document . cache_get ( "textDocument/codeLens" ) )
771771 end
772772
773+ def test_locating_a_non_existing_location_raises
774+ document = RubyLsp ::RubyDocument . new ( source : <<~RUBY . chomp , version : 1 , uri : URI ( "file:///foo/bar.rb" ) )
775+ class Foo
776+ end
777+ RUBY
778+
779+ # Exactly at the last character doesn't raise
780+ document . locate_node ( { line : 1 , character : 2 } )
781+
782+ # Anything beyond does
783+ error = assert_raises ( RubyLsp ::Document ::LocationNotFoundError ) do
784+ document . locate_node ( { line : 3 , character : 2 } )
785+ end
786+
787+ assert_equal ( <<~MESSAGE . chomp , error . message )
788+ Requested position: {:line=>3, :character=>2}
789+ Source:
790+
791+ class Foo
792+ end
793+ MESSAGE
794+ end
795+
773796 private
774797
775798 def assert_error_edit ( actual , error_range )
Original file line number Diff line number Diff line change @@ -714,6 +714,36 @@ def handle_window_show_message_response(title)
714714 end
715715 end
716716
717+ def test_requests_to_a_non_existing_position_return_error
718+ uri = URI ( "file:///foo.rb" )
719+
720+ @server . process_message ( {
721+ method : "textDocument/didOpen" ,
722+ params : {
723+ textDocument : {
724+ uri : uri ,
725+ text : "class Foo\n end" ,
726+ version : 1 ,
727+ languageId : "ruby" ,
728+ } ,
729+ } ,
730+ } )
731+
732+ @server . process_message ( {
733+ id : 1 ,
734+ method : "textDocument/completion" ,
735+ params : {
736+ textDocument : {
737+ uri : uri ,
738+ } ,
739+ position : { line : 10 , character : 0 } ,
740+ } ,
741+ } )
742+
743+ error = find_message ( RubyLsp ::Error )
744+ assert_match ( "Request textDocument/completion failed to find the target position." , error . message )
745+ end
746+
717747 private
718748
719749 def with_uninstalled_rubocop ( &block )
You can’t perform that action at this time.
0 commit comments