diff --git a/README.md b/README.md index 5d0fa906..059208bd 100644 --- a/README.md +++ b/README.md @@ -1421,6 +1421,7 @@ Language scopes: - def: Function definitions (*all* `def` block in their entirety) - async-def: Async function definitions (*all* `async def` block in their entirety) + - methods: Function definitions inside `class` bodies --python-query Scope Python code using a custom tree-sitter query. diff --git a/src/scoping/langs/python.rs b/src/scoping/langs/python.rs index c708d1f3..ea95ae60 100644 --- a/src/scoping/langs/python.rs +++ b/src/scoping/langs/python.rs @@ -25,15 +25,18 @@ pub enum PreparedPythonQuery { FunctionNames, /// Function calls. FunctionCalls, - /// Class definitions (in their entirety) + /// Class definitions (in their entirety). Class, - /// Function definitions (*all* `def` block in their entirety) + /// Function definitions (*all* `def` block in their entirety). Def, - /// Async function definitions (*all* `async def` block in their entirety) + /// Async function definitions (*all* `async def` block in their entirety). AsyncDef, + /// Function definitions inside `class` bodies. + Methods, } impl From for TSQuery { + #[allow(clippy::too_many_lines)] fn from(value: PreparedPythonQuery) -> Self { Self::new( &Python::lang(), @@ -94,6 +97,18 @@ impl From for TSQuery { PreparedPythonQuery::AsyncDef => { r#"((function_definition) @def (#match? @def "^async "))"# } + PreparedPythonQuery::Methods => { + r" + (class_definition + body: (block + [ + (function_definition) @method + (decorated_definition definition: (function_definition)) @method + ] + ) + ) + " + } }, ) .expect("Prepared queries to be valid") diff --git a/tests/langs/mod.rs b/tests/langs/mod.rs index 994b0ca9..3446d2cb 100644 --- a/tests/langs/mod.rs +++ b/tests/langs/mod.rs @@ -117,6 +117,11 @@ impl InScopeLinePart { include_str!("python/base.py"), Python::new(CodeQuery::Prepared(PreparedPythonQuery::AsyncDef)), )] +#[case( + "base.py_methods", + include_str!("python/base.py"), + Python::new(CodeQuery::Prepared(PreparedPythonQuery::Methods)), +)] #[case( "base.ts_strings", include_str!("typescript/base.ts"), diff --git a/tests/langs/snapshots/r#mod__langs__base.py_methods.snap b/tests/langs/snapshots/r#mod__langs__base.py_methods.snap new file mode 100644 index 00000000..4b9408da --- /dev/null +++ b/tests/langs/snapshots/r#mod__langs__base.py_methods.snap @@ -0,0 +1,73 @@ +--- +source: tests/langs/mod.rs +expression: inscope_parts +--- +- n: 51 + l: " @staticmethod\n" + m: " ^^^^^^^^^^^^^^^" +- n: 52 + l: " def static_decorator(func):\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 53 + l: " \"\"\"Decorator for static methods.\"\"\"\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 54 + l: "\n" + m: ^^ +- n: 55 + l: " def wrapper(*args, **kwargs):\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 56 + l: " print(\"Static method decorator called\")\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 57 + l: " return func(*args, **kwargs)\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 58 + l: "\n" + m: ^^ +- n: 59 + l: " return wrapper\n" + m: "^^^^^^^^^^^^^^^^^^^^^^ " +- n: 62 + l: " @classmethod\n" + m: " ^^^^^^^^^^^^^^" +- n: 63 + l: " def class_method(cls) -> None:\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 64 + l: " \"\"\"Class method.\"\"\"\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 65 + l: " cls.class_var += \" updated\"\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 66 + l: " print(f\"Class variable is now {cls.class_var}\")\n" + m: "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ " +- n: 69 + l: " def instance_method(self) -> None:\n" + m: " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" +- n: 70 + l: " \"\"\"Instance method.\"\"\"\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 71 + l: " self.instance_var = \"Instance variable\"\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 72 + l: " print(f\"Instance variable is {self.instance_var}\")\n" + m: "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ " +- n: 74 + l: " @staticmethod\n" + m: " ^^^^^^^^^^^^^^^" +- n: 75 + l: " @static_decorator\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^ +- n: 76 + l: " def static_method() -> None:\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 77 + l: " \"\"\"Static method.\"\"\"\n" + m: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- n: 78 + l: " print(\"Inside static method\")\n" + m: "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "