fix(ts): improve ruby handling of singletons and methods (#293)

Ruby allows us to do a lot of weird stuff, and having weirdness glaring
at us from the side panel can be handy for when we sober up and start
cleaning.

This commit handles a number of singleton class manipulation cases,
as well as adds a reciever information to singleton method declarations.
This commit is contained in:
Slotos 2023-09-18 19:40:21 +02:00 committed by GitHub
parent fa8c408b76
commit a2368d1c4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 389 additions and 1 deletions

View file

@ -226,6 +226,19 @@ M.rust = {
M.ruby = {
postprocess = function(bufnr, item, match)
-- Reciever modification comes first, as we intend for it to generate a ruby-like `reciever.name`
local receiver = (utils.get_at_path(match, "receiver") or {}).node
local separator = (utils.get_at_path(match, "separator") or {}).node
if receiver then
local reciever_name = get_node_text(receiver, bufnr) or "<parse error>"
local separator_string = get_node_text(separator, bufnr) or "."
if reciever_name ~= item.name then
item.name = reciever_name .. separator_string .. item.name
end
end
-- Method modification comes last, as it's supposed to generate a global prefix
-- akin to RSpec's "describe ClassName"
local method = (utils.get_at_path(match, "method") or {}).node
if method then
local fn = get_node_text(method, bufnr) or "<parse error>"

View file

@ -20,10 +20,17 @@
) @type
(singleton_method
name: (identifier) @name
object: [(constant) (self) (identifier)] @receiver
(["." "::"] @separator)?
name: [(operator) (identifier)] @name
(#set! "kind" "Method")
) @type
(singleton_class
value: (_) @name
(#set! "kind" "Class")
) @type
(module
name: [(constant) (scope_resolution)] @name
(#set! "kind" "Module")

View file

@ -241,5 +241,325 @@ describe("treesitter ruby", function()
},
},
})
-- Singleton methods test
util.test_file_symbols("treesitter", "./tests/treesitter/ruby_test_singleton.rb", {
{
kind = "Class",
name = "Klassy",
level = 0,
lnum = 1,
col = 0,
end_lnum = 11,
end_col = 3,
children = {
{
kind = "Class",
name = "self",
level = 1,
lnum = 2,
col = 2,
end_lnum = 6,
end_col = 5,
children = {
{
kind = "Method",
name = "==",
level = 2,
lnum = 3,
col = 4,
end_lnum = 3,
end_col = 22,
},
{
kind = "Method",
name = "regular",
level = 2,
lnum = 4,
col = 4,
end_lnum = 4,
end_col = 20,
},
{
kind = "Method",
name = "endless",
level = 2,
lnum = 5,
col = 4,
end_lnum = 5,
end_col = 21,
},
},
},
{
kind = "Method",
name = "self.<=>",
level = 1,
lnum = 8,
col = 2,
end_lnum = 8,
end_col = 26,
},
{
kind = "Method",
name = "self.regularish",
level = 1,
lnum = 9,
col = 2,
end_lnum = 9,
end_col = 26,
},
{
kind = "Method",
name = "self.endlessish",
level = 1,
lnum = 10,
col = 2,
end_lnum = 10,
end_col = 27,
},
},
},
-- Explody
{
kind = "Method",
name = "Explody.>",
level = 0,
lnum = 13,
col = 0,
end_lnum = 13,
end_col = 25,
},
{
kind = "Method",
name = "Explody.regularnot",
level = 0,
lnum = 14,
col = 0,
end_lnum = 14,
end_col = 27,
},
{
kind = "Method",
name = "Explody.endlessness",
level = 0,
lnum = 15,
col = 0,
end_lnum = 15,
end_col = 29,
},
-- instance
{
kind = "Method",
name = "instance.==",
level = 0,
lnum = 17,
col = 0,
end_lnum = 17,
end_col = 27,
},
{
kind = "Method",
name = "instance.regular",
level = 0,
lnum = 18,
col = 0,
end_lnum = 18,
end_col = 25,
},
{
kind = "Method",
name = "instance.endless",
level = 0,
lnum = 19,
col = 0,
end_lnum = 19,
end_col = 26,
},
-- double colon instead of dot
{
kind = "Method",
name = "youcandothistoo::==",
level = 0,
lnum = 21,
col = 0,
end_lnum = 21,
end_col = 35,
},
{
kind = "Method",
name = "youcandothistoo::regular",
level = 0,
lnum = 22,
col = 0,
end_lnum = 22,
end_col = 33,
},
{
kind = "Method",
name = "youcandothistoo::endless",
level = 0,
lnum = 23,
col = 0,
end_lnum = 23,
end_col = 34,
},
-- value eigenclass
{
kind = "Class",
name = "42",
level = 0,
lnum = 26,
col = 0,
end_lnum = 30,
end_col = 3,
children = {
{
kind = "Method",
name = "==",
level = 1,
lnum = 27,
col = 2,
end_lnum = 27,
end_col = 20,
},
{
kind = "Method",
name = "regular",
level = 1,
lnum = 28,
col = 2,
end_lnum = 28,
end_col = 18,
},
{
kind = "Method",
name = "endless",
level = 1,
lnum = 29,
col = 2,
end_lnum = 29,
end_col = 19,
},
},
},
-- string value eigenclass
{
kind = "Class",
name = '"42"',
level = 0,
lnum = 32,
col = 0,
end_lnum = 36,
end_col = 3,
children = {
{
kind = "Method",
name = "==",
level = 1,
lnum = 33,
col = 2,
end_lnum = 33,
end_col = 20,
},
{
kind = "Method",
name = "regular",
level = 1,
lnum = 34,
col = 2,
end_lnum = 34,
end_col = 18,
},
{
kind = "Method",
name = "endless",
level = 1,
lnum = 35,
col = 2,
end_lnum = 35,
end_col = 19,
},
},
},
-- variable eigenclass
{
kind = "Class",
name = "variable",
level = 0,
lnum = 38,
col = 0,
end_lnum = 42,
end_col = 3,
children = {
{
kind = "Method",
name = "==",
level = 1,
lnum = 39,
col = 2,
end_lnum = 39,
end_col = 20,
},
{
kind = "Method",
name = "regular",
level = 1,
lnum = 40,
col = 2,
end_lnum = 40,
end_col = 18,
},
{
kind = "Method",
name = "endless",
level = 1,
lnum = 41,
col = 2,
end_lnum = 41,
end_col = 19,
},
},
},
-- constant/class/module eigenclass
{
kind = "Class",
name = "Constant",
level = 0,
lnum = 44,
col = 0,
end_lnum = 48,
end_col = 3,
children = {
{
kind = "Method",
name = "==",
level = 1,
lnum = 45,
col = 2,
end_lnum = 45,
end_col = 20,
},
{
kind = "Method",
name = "regular",
level = 1,
lnum = 46,
col = 2,
end_lnum = 46,
end_col = 18,
},
{
kind = "Method",
name = "endless",
level = 1,
lnum = 47,
col = 2,
end_lnum = 47,
end_col = 19,
},
},
},
})
end)
end)

View file

@ -0,0 +1,48 @@
class Klassy
class << self
def ==(other); end
def regular; end
def endless = nil
end
def self.<=>(other); end
def self.regularish; end
def self.endlessish = nil
end
def Explody.>(other); end
def Explody.regularnot; end
def Explody.endlessness = nil
def instance.==(other); end
def instance.regular; end
def instance.endless = nil
def youcandothistoo::==(other); end
def youcandothistoo::regular; end
def youcandothistoo::endless = nil
# This will actually explode in Ruby, but only because integers are special
class << 42
def ==(other); end
def regular; end
def endless = nil
end
class << "42"
def ==(other); end
def regular; end
def endless = nil
end
class << variable
def ==(other); end
def regular; end
def endless = nil
end
class << Constant
def ==(other); end
def regular; end
def endless = nil
end