ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: "bkuhlmann (Brooke Kuhlmann) via ruby-core" <ruby-core@ml.ruby-lang.org>
To: ruby-core@ml.ruby-lang.org
Cc: "bkuhlmann (Brooke Kuhlmann)" <noreply@ruby-lang.org>
Subject: [ruby-core:120526] [Ruby master Feature#21005] Update the source location method to include line start/stop and column start/stop details
Date: Tue, 07 Jan 2025 15:23:30 +0000 (UTC)	[thread overview]
Message-ID: <redmine.journal-111330.20250107152330.40939@ruby-lang.org> (raw)
In-Reply-To: <redmine.issue-21005.20250105210016.40939@ruby-lang.org>

Issue #21005 has been updated by bkuhlmann (Brooke Kuhlmann).


**Yusuke**: I went hunting for the `Prism.node_for` method/documentation but couldn't find it. If I understand you correctly, I think you are proposing adding the `.node_for` implementation to Prism? If so, I like the detailed information in your example especially when dealing with heredocs and being able to access content location. That would yield even greater flexibility for anyone implementing atop this data. To be more exact, I'm guessing the following could be amended to what Benoit proposed above then?:

- content_line_start
- content_line_end
- content_column_start
- content_column_end

By the way, one slight correction to what you were asking above. This is what my DSL generates:

``` ruby
# DSL
class Demo
  include Initable[[:key, :default, proc { Object.new }]]
end

# End result.
def initialize(default: Object.new)
  @default = default
end
```

The emphasis is on `key` because `key` is an *optional* keyword parameter as defined in the [Method#parameters](https://docs.ruby-lang.org/en/master/Method.html#method-i-parameters) implementation. Essentially, my DSL reconstitutes the raw parameters answered back into a method signature where the third element in the 3-tuple is additional sugar (specific to my DSL) for providing a default value since `Method#parameters` only answers an array of tuples.

**Jean**

> I fear it may break code that splats the array

Ugh, yes. What about this instead?

``` ruby
proc.source_location(as: :verbose) 
```

This would give us room to expand the `as` keyword with different values to support different object shapes in the future (assuming this design expands and grows in future Ruby versions) without backing us into a corner.

> Returning an actual object with named methods make it easier to understand what's being used and easier to extend later on.

Yeah, this would be most welcome. I know I gave an example of using a `Hash` above but icing on the cake would be to answer a `Data` instead object so you could simply ask for whatever attribute you need.




----------------------------------------
Feature #21005: Update the source location method to include line start/stop and column start/stop details
https://bugs.ruby-lang.org/issues/21005#change-111330

* Author: bkuhlmann (Brooke Kuhlmann)
* Status: Open
----------------------------------------
## Why

👋 Hello. After discussing with Kevin Newton and Benoit Daloze in [Feature 20999](https://bugs.ruby-lang.org/issues/20999), I'd like to propose adding line start/stop and column start/stop information to the `#source_location` method for the following objects:

- [Binding](https://docs.ruby-lang.org/en/master/Binding.html)
- [Proc](https://docs.ruby-lang.org/en/master/Proc.html)
- [Method](https://docs.ruby-lang.org/en/master/Method.html)
- [UnboundMethod](https://docs.ruby-lang.org/en/master/UnboundMethod.html)

At the moment, when using `#source_location`, you only get the following information:

``` ruby
def demo = "A demonstration."

# From disk.
method(:demo).source_location  # ["/Users/bkuhlmann/Engineering/Misc/demo", 15]

# From memory.
method(:demo).source_location  # ["(irb)", 3]
```

Notice, when asking for the source location, we only get the path/location as the first element and the line number as the second element but I'd like to obtain a much richer set of data which includes line start/stop and column start/stop so I can avoid leaning on the `RubyVM` for this information. Example:

``` ruby
def demo = "A demonstration."

# From disk.
instructions = RubyVM::InstructionSequence.of method(:demo)
puts [instructions.absolute_path, *instructions.to_a.dig(4, :code_location)]

[
  "/Users/bkuhlmann/Engineering/Misc/demo",  # Source path.
  15,                                        # Line start.
  0,                                         # Column start.
  15,                                        # Line stop.
  29                                         # Column stop.
]

# From memory.
instructions = RubyVM::InstructionSequence.of method(:demo)
puts instructions.script_lines

[
  "def demo = \"A demonstration.\"\n",
  ""
]
```

By having access to the path (or lack thereof in case of IRB), line start/stop, and column start/stop, this means we could avoid using the RubyVM to obtain raw source code for any of these objects. This would not only enhance debugging situations but also improve Domain Specific Languages that wish to leverage this information for introducing new features and/or new debugging capabilities to the language.

## How

Building upon the examples provided above, I'd like to see `Binding`, `Proc`, `Method`, and `UnboundMethod` respond to `#source_location` as follows:

``` ruby
[
  "/Users/bkuhlmann/Engineering/Misc/demo",  # Source path.
  15,                                        # Line start.
  15,                                        # Line stop.
  0,                                         # Column start.
  29                                         # Column stop.
]
```

Notice, for data grouping purposes, I changed the array structure to always start with the path as the first element, followed by line information, and ending with column information. Alternatively, it could might be nice to improve upon the above by answering a hash each time, instead, for a more self-describing data structure. Example:

``` ruby
{
  path: "/Users/bkuhlmann/Engineering/Misc/demo",
  line_start: 15,
  line_stop: 15,
  column_start: 0,
  column_stop: 29
}
```

For in-memory, situations like IRB, it would be nice to answer the equivalent of `RubyVM::InstructionSequence#script_lines` which would always be an `Array` with no line or column information since only the source code is necessary. Example:

``` ruby
[
  "def demo = \"A demonstration.\"\n",
  ""
]
```

From a pattern matching perspective, this could provide the best of both worlds especially if information is answered as either a `Hash` or and `Array`. Example:

``` 
def demo = "A demonstration."

case method(:demo).source_location
  in Hash then puts "Source information obtained from disk."
  in Array then puts "Source obtained from memory."
  else fail TypeError, "Unrecognized source location type."
end
```

This above is only a simple example but there's a lot we could do with this information if the above pattern match was enhanced to deal with the extraction and formatting of the actual source code!

## Notes

This feature request is related to the following discussions in case more context is of help:

- [Feature 6012](https://bugs.ruby-lang.org/issues/6012)
- [Feature 20999](https://bugs.ruby-lang.org/issues/20999)




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

  parent reply	other threads:[~2025-01-07 15:24 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-05 21:00 [ruby-core:120488] " bkuhlmann (Brooke Kuhlmann) via ruby-core
2025-01-06 14:39 ` [ruby-core:120504] " Eregon (Benoit Daloze) via ruby-core
2025-01-06 20:20 ` [ruby-core:120508] " bkuhlmann (Brooke Kuhlmann) via ruby-core
2025-01-07  8:51 ` [ruby-core:120517] " mame (Yusuke Endoh) via ruby-core
2025-01-07 11:12 ` [ruby-core:120521] " byroot (Jean Boussier) via ruby-core
2025-01-07 15:23 ` bkuhlmann (Brooke Kuhlmann) via ruby-core [this message]
2025-01-07 20:04 ` [ruby-core:120534] " byroot (Jean Boussier) via ruby-core
2025-01-07 20:28 ` [ruby-core:120536] " Earlopain (Earlopain _) via ruby-core
2025-01-08  0:40 ` [ruby-core:120540] " tenderlovemaking (Aaron Patterson) via ruby-core
2025-01-08  0:49 ` [ruby-core:120541] " tenderlovemaking (Aaron Patterson) via ruby-core
2025-01-08  1:24 ` [ruby-core:120542] " mame (Yusuke Endoh) via ruby-core
2025-01-08  4:55 ` [ruby-core:120550] " tenderlovemaking (Aaron Patterson) via ruby-core
2025-01-08 10:24 ` [ruby-core:120555] " Eregon (Benoit Daloze) via ruby-core
2025-01-08 14:15 ` [ruby-core:120557] " Dan0042 (Daniel DeLorme) via ruby-core
2025-01-08 17:22 ` [ruby-core:120559] " tenderlovemaking (Aaron Patterson) via ruby-core
2025-01-09 14:36 ` [ruby-core:120578] " Eregon (Benoit Daloze) via ruby-core

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.journal-111330.20250107152330.40939@ruby-lang.org \
    --to=ruby-core@ml.ruby-lang.org \
    --cc=noreply@ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).