From ba6d65b458121de1ed509e34dc693c36b03867e4 Mon Sep 17 00:00:00 2001 From: Matthew Bennett-Lovesey Date: Fri, 25 Aug 2023 21:03:33 +0100 Subject: [PATCH] Add Ruby Query and test case (#22) * add ruby query * Add test and update tree-sitter query for Ruby * Remove #strip! and rename test objects for easier debugging * Mark requires as imports, simplify method call query * Make sure the ruby test_file actually executes as a ruby script * Mark method params as optional in ruby * Modules can have comments in ruby, just like classes * Add aliases to ruby tags and test files * Include select-adjacent for docs on ruby modules, add :: as lineage * Apply @pqn's lineage suggestion to ruby query and fix incorrect alias_method --------- Co-authored-by: daviddale Co-authored-by: Matthew Bennett-Lovesey --- goldens/test.rb.golden | 240 +++++++++++++++++++++++++++++++++++++++++ queries/ruby_tags.scm | 58 ++++++++++ test_files/test.rb | 72 +++++++++++++ 3 files changed, 370 insertions(+) create mode 100644 goldens/test.rb.golden create mode 100644 queries/ruby_tags.scm create mode 100644 test_files/test.rb diff --git a/goldens/test.rb.golden b/goldens/test.rb.golden new file mode 100644 index 0000000..5415284 --- /dev/null +++ b/goldens/test.rb.golden @@ -0,0 +1,240 @@ +Name: require +Definition (definition.import): +require "net/http" + +Name: SomeClass +Doc: +# This is just your standard class +# nothing strange here +Declaration (definition.class): +class SomeClass + # In ruby, the parentheses can be omitted in many circumstances + # This is probably a job for the AST, but we'll add a test here anyway + def empty_parens() + "Nice" + end +end + +Name: empty_parens +Parameters: () +Definition (definition.method): +def empty_parens() + "Nice" + end +Lineage: [SomeClass] +Lineage types: [class] + +Name: SomeModule +Definition (definition.module): +module SomeModule + def omitted_parens + :foo + end +end + +Name: omitted_parens +Definition (definition.method): +def omitted_parens + :foo + end +Lineage: [SomeModule] +Lineage types: [module] + +Name: Test +Definition (definition.module): +module Test ; end + +Name: SubTest +Doc: +# This module is acting more like a namespace +Definition (definition.module): +module Test::SubTest + # We're inheriting from SomeClass here + class MyClass < SomeClass + include SomeModule + + # Some languages would call this a "static" method + def self.in_eigenclass + new.simple + end + + class << self + # In all ways, this method behaves the same as `in_eigenclass` + def in_reopened_eigenclass + new.optional_param(true) + end + end + + def simple + self + end + alias :not_so_simple :simple + alias_method :really_not_so_simple, :simple + + def unparenthesized arg1, arg2 + arg2, arg1 = arg1, arg2 + end + + def optional_param(arg1, arg2 = "fine") + nil + end + + def splatted_args(arg1, *args) + end + end +end +Lineage: [Test] +Lineage types: [unknown] + +Name: MyClass +Declaration (definition.class): +class MyClass < SomeClass + include SomeModule + + # Some languages would call this a "static" method + def self.in_eigenclass + new.simple + end + + class << self + # In all ways, this method behaves the same as `in_eigenclass` + def in_reopened_eigenclass + new.optional_param(true) + end + end + + def simple + self + end + alias :not_so_simple :simple + alias_method :really_not_so_simple, :simple + + def unparenthesized arg1, arg2 + arg2, arg1 = arg1, arg2 + end + + def optional_param(arg1, arg2 = "fine") + nil + end + + def splatted_args(arg1, *args) + end + end +Lineage: [Test SubTest] +Lineage types: [unknown module] + +Name: include +Reference (reference.call): +include SomeModule + +Name: in_eigenclass +Doc: +# Some languages would call this a "static" method +Definition (definition.method): +def self.in_eigenclass + new.simple + end +Lineage: [Test SubTest MyClass] +Lineage types: [unknown module class] + +Name: simple +Reference (reference.call): +new.simple + +Name: in_reopened_eigenclass +Definition (definition.method): +def in_reopened_eigenclass + new.optional_param(true) + end +Lineage: [Test SubTest MyClass] +Lineage types: [unknown module class] + +Name: optional_param +Reference (reference.call): +new.optional_param(true) + +Name: simple +Definition (definition.method): +def simple + self + end +Lineage: [Test SubTest MyClass] +Lineage types: [unknown module class] + +Name: :not_so_simple +Declaration (definition.method): +alias :not_so_simple :simple +Lineage: [Test SubTest MyClass] +Lineage types: [unknown module class] + +Name: alias_method +Reference (reference.call): +alias_method :really_not_so_simple, :simple + +Name: unparenthesized +Parameters: arg1, arg2 +Definition (definition.method): +def unparenthesized arg1, arg2 + arg2, arg1 = arg1, arg2 + end +Lineage: [Test SubTest MyClass] +Lineage types: [unknown module class] + +Name: optional_param +Parameters: (arg1, arg2 = "fine") +Definition (definition.method): +def optional_param(arg1, arg2 = "fine") + nil + end +Lineage: [Test SubTest MyClass] +Lineage types: [unknown module class] + +Name: SubSubTest +Definition (definition.module): +module Test::SubTest::SubSubTest + module SubSubSubTest + extend SomeModule + end +end +Lineage: [Test SubTest] +Lineage types: [unknown unknown] + +Name: SubSubSubTest +Definition (definition.module): +module SubSubSubTest + extend SomeModule + end +Lineage: [Test SubTest SubSubTest] +Lineage types: [unknown unknown module] + +Name: extend +Reference (reference.call): +extend SomeModule + +Name: MyTerribleMethod +Reference (reference.call): +MyTerribleMethod() + +Name: new +Reference (reference.call): +Test::SubTest::MyClass.new + +Name: splatted_args +Reference (reference.call): +Test::SubTest::MyClass.new.splatted_args 1, 2, 3 + +Name: new +Reference (reference.call): +Test::SubTest::MyClass.new + +Name: not_so_simple +Reference (reference.call): +Test::SubTest::MyClass.new.not_so_simple + +Name: in_reopened_eigenclass +Reference (reference.call): +Test::SubTest::MyClass.in_reopened_eigenclass + +Name: omitted_parens +Reference (reference.call): +Test::SubTest::SubSubTest::SubSubSubTest.omitted_parens diff --git a/queries/ruby_tags.scm b/queries/ruby_tags.scm new file mode 100644 index 0000000..8c3c135 --- /dev/null +++ b/queries/ruby_tags.scm @@ -0,0 +1,58 @@ +; Method definitions + +( + (comment)* @doc + . + [ + (method + name: (_) @name + parameters: (method_parameters)? @codeium.parameters + body: (_) @body + ) + (singleton_method + name: (_) @name + parameters: (method_parameters)? @codeium.parameters + body: (_) @body + ) + ] @definition.method + (#select-adjacent! @doc @definition.method) +) + +(alias + name: (_) @name) @definition.method + + +; Class definitions + +( + (comment)* @doc + . + (class + name: (_) @name) @definition.class + (#lineage-from-name! "::") + (#select-adjacent! @doc @definition.class) +) + +; Module definitions + +( + (comment)* @doc + . + (module + name: (_) @name) @definition.module + (#lineage-from-name! "::") + (#select-adjacent! @doc @definition.module) +) + +; Calls + +( + call method: [(identifier) (constant)] @name + (#not-match? @name "^(lambda|load|require|require_relative|__FILE__|__LINE__)$") +) @reference.call + + +( + call method: [(identifier) (constant)] @name + (#match? @name "^(require|require_relative)$") +) @definition.import diff --git a/test_files/test.rb b/test_files/test.rb new file mode 100644 index 0000000..d1d66f7 --- /dev/null +++ b/test_files/test.rb @@ -0,0 +1,72 @@ +require "net/http" + +# This is just your standard class +# nothing strange here +class SomeClass + # In ruby, the parentheses can be omitted in many circumstances + # This is probably a job for the AST, but we'll add a test here anyway + def empty_parens() + "Nice" + end +end + +module SomeModule + def omitted_parens + :foo + end +end + +module Test ; end + +# This module is acting more like a namespace +module Test::SubTest + # We're inheriting from SomeClass here + class MyClass < SomeClass + include SomeModule + + # Some languages would call this a "static" method + def self.in_eigenclass + new.simple + end + + class << self + # In all ways, this method behaves the same as `in_eigenclass` + def in_reopened_eigenclass + new.optional_param(true) + end + end + + def simple + self + end + alias :not_so_simple :simple + alias_method :really_not_so_simple, :simple + + def unparenthesized arg1, arg2 + arg2, arg1 = arg1, arg2 + end + + def optional_param(arg1, arg2 = "fine") + nil + end + + def splatted_args(arg1, *args) + end + end +end + +module Test::SubTest::SubSubTest + module SubSubSubTest + extend SomeModule + end +end + +def MyTerribleMethod +end + +# Some calls, just to check everything works +MyTerribleMethod() +Test::SubTest::MyClass.new.splatted_args 1, 2, 3 +Test::SubTest::MyClass.new.not_so_simple +Test::SubTest::MyClass.in_reopened_eigenclass +Test::SubTest::SubSubTest::SubSubSubTest.omitted_parens