Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/rbtr/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ dependencies = [
"tree-sitter-java",
"tree-sitter-javascript",
"tree-sitter-json",
"tree-sitter-less",
"tree-sitter-markdown",
"tree-sitter-python",
"tree-sitter-rst",
"tree-sitter-ruby",
"tree-sitter-rust",
"tree-sitter-scss",
"tree-sitter-sql",
"tree-sitter-toml",
"tree-sitter-typescript",
Expand Down
4 changes: 4 additions & 0 deletions packages/rbtr/src/rbtr/languages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@
from rbtr.languages.java import JavaPlugin
from rbtr.languages.javascript import JavaScriptPlugin
from rbtr.languages.json import JsonPlugin
from rbtr.languages.less import LessPlugin
from rbtr.languages.markdown import MarkdownPlugin
from rbtr.languages.python import PythonPlugin
from rbtr.languages.rst import RstPlugin
from rbtr.languages.ruby import RubyPlugin
from rbtr.languages.rust import RustPlugin
from rbtr.languages.scss import ScssPlugin
from rbtr.languages.sql import SqlPlugin
from rbtr.languages.toml import TomlPlugin
from rbtr.languages.yaml import YamlPlugin
Expand Down Expand Up @@ -112,13 +114,15 @@ def _register_builtins(self) -> None:
HclPlugin,
HtmlPlugin,
JavaPlugin,
LessPlugin,
JavaScriptPlugin,
JsonPlugin,
MarkdownPlugin,
PythonPlugin,
RstPlugin,
RubyPlugin,
RustPlugin,
ScssPlugin,
SqlPlugin,
TomlPlugin,
YamlPlugin,
Expand Down
68 changes: 68 additions & 0 deletions packages/rbtr/src/rbtr/languages/less.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""Less language plugin.

Extends CSS extraction with the Less preprocessor constructs,
via a tree-sitter query. Rule sets and `@media`/`@charset`/
`@keyframes` are captured as doc sections; `@`-prefixed
declarations as variables; `.mixin()` definitions as functions;
`@import` as imports for cross-file edges. Mixin *calls*
(`mixin_statement`) are references, not definitions, and are
skipped.

Extracted chunks::

@primary: #333; → variable "@primary"
.rounded(@r) { ... } → function "rounded"
.btn { ... } → doc_section ".btn"
@keyframes slide { ... } → doc_section "slide"
@import "reset"; → import, metadata {module: "reset"}
"""

from __future__ import annotations

from rbtr.languages.css import css_nesting_scope
from rbtr.languages.hookspec import (
LanguageRegistration,
build_quoted_import,
hookimpl,
)

_QUERY = r"""
(declaration
(property_name) @_var_name
(#match? @_var_name "^[@]")) @variable

(mixin_definition
(class_name) @_fn_name) @function

(rule_set
(selectors) @_section_name) @doc_section

(media_statement) @doc_section

(charset_statement) @doc_section

(keyframes_statement
(keyframes_name) @_section_name) @doc_section

(import_statement
(string_value) @_import_module) @import
"""


class LessPlugin:
"""Less language support — Less constructs + @import edges."""

@hookimpl
def rbtr_register_languages(self) -> list[LanguageRegistration]:
return [
LanguageRegistration(
id="less",
extensions=frozenset({".less"}),
grammar_module="tree_sitter_less",
query=_QUERY,
scope_extractor=css_nesting_scope,
import_extractor=build_quoted_import,
import_targets=frozenset({"css", "scss", "less"}),
language_plugin_version=1,
),
]
78 changes: 78 additions & 0 deletions packages/rbtr/src/rbtr/languages/scss.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""SCSS / Sass language plugin.

Extends CSS extraction with the Sass preprocessor constructs,
via a tree-sitter query. Rule sets (incl. `%placeholder`
selectors), `@media`/`@charset`/`@keyframes` are captured as
doc sections; `$`-prefixed declarations as variables; `@mixin`
and `@function` definitions as functions; `@use`/`@forward`/
`@import` as imports for cross-file edges.

Extracted chunks::

$primary: #333; → variable "$primary"
@mixin flex($dir) { ... } → function "flex"
@function rem($px) { ... } → function "rem"
%card { ... } → doc_section "%card"
.btn { ... } → doc_section ".btn"
@keyframes slide { ... } → doc_section "slide"
@use "config"; → import, metadata {module: "config"}
"""

from __future__ import annotations

from rbtr.languages.css import css_nesting_scope
from rbtr.languages.hookspec import (
LanguageRegistration,
build_quoted_import,
hookimpl,
)

_QUERY = r"""
(declaration
(property_name) @_var_name
(#match? @_var_name "^[$]")) @variable

(mixin_statement
(identifier) @_fn_name) @function

(function_statement
(identifier) @_fn_name) @function

(rule_set
(selectors) @_section_name) @doc_section

(media_statement) @doc_section

(charset_statement) @doc_section

(keyframes_statement
(keyframes_name) @_section_name) @doc_section

(use_statement
(string_value) @_import_module) @import

(forward_statement
(string_value) @_import_module) @import

(import_statement
(string_value) @_import_module) @import
"""


class ScssPlugin:
"""SCSS language support — Sass constructs + @use/@forward edges."""

@hookimpl
def rbtr_register_languages(self) -> list[LanguageRegistration]:
return [
LanguageRegistration(
id="scss",
extensions=frozenset({".scss"}),
grammar_module="tree_sitter_scss",
query=_QUERY,
scope_extractor=css_nesting_scope,
import_extractor=build_quoted_import,
import_targets=frozenset({"css", "scss", "less"}),
language_plugin_version=1,
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
"less.less::@import \"base\"; -> base.less::@bg [imports]"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
"scss.scss::@forward \"theme\"; -> theme.scss::$radius [imports]",
"scss.scss::@import \"reset\"; -> reset.scss::* [imports]",
"scss.scss::@use \"config\"; -> config.scss::$brand [imports]"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
[
{
"id": "8f10aa0b97fda5ea",
"blob_sha": "sha1",
"file_path": "base.less",
"kind": "variable",
"name": "@bg",
"scope": "",
"language": "less",
"content": "@bg: #ffffff;",
"line_start": 3,
"line_end": 3,
"metadata": {
"module": "",
"names": "",
"dots": "",
"language_hint": ""
}
},
{
"id": "14b448311d1139b8",
"blob_sha": "sha1",
"file_path": "less.less",
"kind": "import",
"name": "@import \"base\";",
"scope": "",
"language": "less",
"content": "@import \"base\";",
"line_start": 7,
"line_end": 7,
"metadata": {
"module": "base",
"names": "",
"dots": "",
"language_hint": ""
}
},
{
"id": "8d9441ee2a311c25",
"blob_sha": "sha1",
"file_path": "less.less",
"kind": "variable",
"name": "@primary",
"scope": "",
"language": "less",
"content": "@primary: #333;",
"line_start": 9,
"line_end": 9,
"metadata": {
"module": "",
"names": "",
"dots": "",
"language_hint": ""
}
},
{
"id": "7c7b0d1e3f708b87",
"blob_sha": "sha1",
"file_path": "less.less",
"kind": "variable",
"name": "@spacing",
"scope": "",
"language": "less",
"content": "@spacing: 8px;",
"line_start": 10,
"line_end": 10,
"metadata": {
"module": "",
"names": "",
"dots": "",
"language_hint": ""
}
},
{
"id": "70cc768af8829886",
"blob_sha": "sha1",
"file_path": "less.less",
"kind": "function",
"name": "rounded",
"scope": "",
"language": "less",
"content": ".rounded(@radius: 4px) {\n border-radius: @radius;\n}",
"line_start": 12,
"line_end": 14,
"metadata": {
"module": "",
"names": "",
"dots": "",
"language_hint": ""
}
},
{
"id": "787412e930624b62",
"blob_sha": "sha1",
"file_path": "less.less",
"kind": "doc_section",
"name": ".greeter",
"scope": "",
"language": "less",
"content": ".greeter {\n color: @primary;\n padding: @spacing;\n .rounded(8px);\n .greeting-label {\n text-transform: uppercase;\n }\n}",
"line_start": 16,
"line_end": 23,
"metadata": {
"module": "",
"names": "",
"dots": "",
"language_hint": ""
}
},
{
"id": "a21acf50441166ff",
"blob_sha": "sha1",
"file_path": "less.less",
"kind": "doc_section",
"name": ".greeting-label",
"scope": ".greeter",
"language": "less",
"content": ".greeting-label {\n text-transform: uppercase;\n }",
"line_start": 20,
"line_end": 22,
"metadata": {
"module": "",
"names": "",
"dots": "",
"language_hint": ""
}
},
{
"id": "289a7c82bd01f03c",
"blob_sha": "sha1",
"file_path": "less.less",
"kind": "doc_section",
"name": "<anonymous>",
"scope": "",
"language": "less",
"content": "@media (max-width: 600px) {\n .greeter {\n font-size: 14px;\n }\n}",
"line_start": 25,
"line_end": 29,
"metadata": {
"module": "",
"names": "",
"dots": "",
"language_hint": ""
}
},
{
"id": "a4d6c82c63beab1c",
"blob_sha": "sha1",
"file_path": "less.less",
"kind": "doc_section",
"name": ".greeter",
"scope": "",
"language": "less",
"content": ".greeter {\n font-size: 14px;\n }",
"line_start": 26,
"line_end": 28,
"metadata": {
"module": "",
"names": "",
"dots": "",
"language_hint": ""
}
},
{
"id": "4d89902ee8dcd396",
"blob_sha": "sha1",
"file_path": "less.less",
"kind": "doc_section",
"name": "greeting-fade",
"scope": "",
"language": "less",
"content": "@keyframes greeting-fade {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}",
"line_start": 31,
"line_end": 38,
"metadata": {
"module": "",
"names": "",
"dots": "",
"language_hint": ""
}
}
]
Loading