feat: inspect TreeSitter tag queries for Dervish universal AST extraction
- Fetch TAGS_QUERY and HIGHLIGHTS_QUERY from 8 official TreeSitter language packages - Save all curated queries as .scm files - Analysis confirms universal query approach is viable - TreeSitter silently ignores non-existent node types per language - Add full conversation references (gemini chat markdown files) - Add comprehensive conversation summary with roadmap
This commit is contained in:
parent
28f5f897d5
commit
069c63f2c8
21 changed files with 9531 additions and 0 deletions
2881
references/gemini-chat1.md
Normal file
2881
references/gemini-chat1.md
Normal file
File diff suppressed because it is too large
Load diff
1199
references/gemini-chat2.md
Normal file
1199
references/gemini-chat2.md
Normal file
File diff suppressed because it is too large
Load diff
2232
references/gemini-chat3.md
Normal file
2232
references/gemini-chat3.md
Normal file
File diff suppressed because it is too large
Load diff
1991
references/gemini-chat4.md
Normal file
1991
references/gemini-chat4.md
Normal file
File diff suppressed because it is too large
Load diff
207
references/gemini-conversation-summary.md
Normal file
207
references/gemini-conversation-summary.md
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
# Dervish / BEX — Complete Conversation Summary
|
||||
|
||||
## Overview
|
||||
|
||||
4 Gemini conversations from 2026-07-03 (all from same session, branching at different points).
|
||||
All revolve around BEX grammar inference algorithms and the Dervish MCP tool.
|
||||
|
||||
---
|
||||
|
||||
## Chat 1: "LLMs, Agenten und Schema-Inferenz" (32 turns)
|
||||
**Model:** 3.5 Flash | **Link:** share/8fce4fbdf14a
|
||||
|
||||
### Flow
|
||||
1. Starts with arXiv:1004.2372 (Bex et al. — XML schema inference via k-OREs)
|
||||
2. Gemini proposes 4 application areas for LLM agents:
|
||||
- **① Workflow Discovery** — infer state machines from agent traces
|
||||
- **② Dynamic Schema Generation** — distill JSON/XML schemas from unstructured data for grammar-guided decoding
|
||||
- **③ API Reverse Engineering** — infer API structure from probe calls
|
||||
- **④ Prompt Injection Defense** — detect structural anomalies in incoming data
|
||||
3. User picks **#3** (API reverse engineering), explores MCP tools
|
||||
4. Shifts focus to **#1 extended: Code Generation patterns** (IaC, CI/CD pipelines)
|
||||
5. **Key insight:** BEX can learn the *unwritten conventions* of existing codebases
|
||||
6. User requests Python implementation of all BEX algorithms from paper pseudocode
|
||||
7. Multiple BEX papers explored:
|
||||
- Bex et al. 2010 (k-OREs, the main paper)
|
||||
- Bex, Neven, Vansummeren 2008 (DTD inference — simpler, less powerful)
|
||||
- Bex, Neven, Schwentick, Vansummeren 2010 (concise regex + DTDs)
|
||||
8. **Paradigm shift:** XML → **YAML-native**. YAML is trees too, no XML intermediate needed
|
||||
9. User requests actual pseudocode extraction from PDFs (not AI-generated hallucinations)
|
||||
10. **Escalation:** User frustrated when Gemini can't find real papers about formal grammars helping agents (hallucinates fake sources). Ends with user abandoning Gemini as search engine
|
||||
|
||||
### Key Decisions
|
||||
- Focus on **code generation patterns** as primary application
|
||||
- **YAML-native** approach (no XML intermediate)
|
||||
- Multiple BEX papers needed for full algorithm coverage
|
||||
|
||||
---
|
||||
|
||||
## Chat 2: "Regex Power Beyond Formal Definitions" (14 turns)
|
||||
**Model:** 3.5 Flash | **Link:** share/d100a2d42200
|
||||
|
||||
### Flow
|
||||
1. Regex primitives (concatenation, disjunction, iteration) vs modern regex
|
||||
2. **Grammar Induction** from positive examples only (Gold's Theorem — impossible in general case)
|
||||
3. BEX algorithms as the practical solution to this problem
|
||||
4. Improvements on BEX: interleaving (.), higher k-values
|
||||
5. User: "I have implemented BEX but I'm intrigued by interleaving"
|
||||
6. Dervish README walkthrough
|
||||
7. **embabel agent** discussion: JVM-based agent framework with typed actions
|
||||
8. GOAP (Goal-Oriented Action Planning) + Dervish — infer action preconditions from examples
|
||||
9. **Convention monitoring idea:** Dervish observes agent, detects when patterns become conventions
|
||||
|
||||
### Key Decisions
|
||||
- BEX improvements needed: interleaving, higher k
|
||||
- Dervish could act as **passive convention observer** for agents
|
||||
|
||||
---
|
||||
|
||||
## Chat 3: "Dervish: Grammar Inference for LLM Agents" (32 turns)
|
||||
**Model:** 3.1 Pro | **Link:** share/0f836d3c25ba
|
||||
|
||||
### Flow (Turns 1-27 — shared with Chat 4)
|
||||
|
||||
#### Phase: README Polish (Turns 1-5)
|
||||
- README paste → improve "MDL" wording
|
||||
- Add **generative schema** section (infer grammar → generate sample data)
|
||||
- Add "Why not just use a schema?" section
|
||||
|
||||
#### Phase: Architecture Discovery (Turns 6-10)
|
||||
- Question: **Can Dervish analyze Java/Kotlin codebases?**
|
||||
- Answer: Language structure ≠ code conventions. Dervish needs *behavioral* sequences
|
||||
- **Turn 7: DomainRouter pre-detection** idea — detect context (source code vs YAML) before choosing algorithm
|
||||
- **Turn 8: MDL Ensemble insight** — Dervish already runs all algorithms and picks best by MDL. No need for pre-detection!
|
||||
- **Turn 9: Single-pass heuristic** — maybe we don't need all algorithms, can derive coverage levels from one pass
|
||||
|
||||
#### Phase: TreeSitter Revolution (Turns 10-17)
|
||||
- **Turn 10: Language-agnostic breakthrough** — Dervish is absolutely language-agnostic, can analyze any codebase
|
||||
- **Turn 11: Prior art needed** — is there existing research on grammar inference from ASTs?
|
||||
- **Turn 12: TreeSitter AST pipeline** — extract AST nodes → flatten to sequences → Dervish infers grammar
|
||||
- **Turn 13-14: Concrete Java examples** — spring boot controllers, which AST nodes matter
|
||||
- **Turn 15: Repository vs directory scoping** — how does Dervish know where conventions boundaries are?
|
||||
- **Turn 16: Avoiding per-language heuristics** — must be zero-config
|
||||
- **Turn 17: Novelty confirmed** — no prior work on grammar inference from TreeSitter ASTs. This is a genuine research contribution
|
||||
|
||||
#### Phase: Roadmap Definition (Turns 18-20)
|
||||
- **Formal roadmap defined:**
|
||||
- **1.0: Code Repository Analysis** — TreeSitter → AST nodes → Dervish grammar
|
||||
- **1.1: Structured Data** (YAML, XML, JSON) — existing feature, enhance
|
||||
- **1.2: Markdown/Document AST** — DISCUSSED THEN **DROPPED** (too fuzzy)
|
||||
- **2.0: Cross-file conventions** — class hierarchies, test patterns, file-level relationships
|
||||
- **1.2 dropped** — documents too fuzzy
|
||||
|
||||
#### Phase: Implementation Design (Turns 21-27)
|
||||
- **Features first, combined later** — code analysis separate from structured data
|
||||
- **Interesting nodes** — NOT per-language heuristic! Use **TreeSitter's built-in tag queries** (tags.scm)
|
||||
- Universal query approach: a single query works across languages (annotation→@meta, call_expression→@call, try_statement→@block)
|
||||
- **Community tags.scm** for 100% coverage (Zero maintenance)
|
||||
- **Single-pass frequency analysis** — count symbol frequency across files, filter outliers < threshold
|
||||
- **Concrete example with Spring Boot:**
|
||||
- Raw AST: `['@RestController', '@RequestMapping', '@PostMapping', 'log.info', 'dto.getItems', 'dto.getItems.isEmpty', 'orderService.process', 'ResponseEntity.ok']`
|
||||
- After frequency filter: remove `dto.getItems` (occurs in 1/10 files)
|
||||
- Result: convention grammar for Spring Boot controllers
|
||||
- **Critical concern (Turn 27):** Will Dervish find genuinely novel insights or just obvious patterns?
|
||||
|
||||
#### Phase: Broader Vision (Turns 28-32 — UNIQUE TO CHAT 3)
|
||||
- **BEX in LLM training/design** — structural tokenization (replace BPE with grammar-guided), Skeleton-of-Thought patterns
|
||||
- **Paper title proposals:**
|
||||
1. "DERVISH: Neuro-Symbolic Code Generation via MDL-Optimized Structural Grammars"
|
||||
2. "AST2Regex: Mining Implicit Conventions from Abstract Syntax Trees"
|
||||
3. "Grammar is All You Need: Zero-Shot Convention Learning for LLMs"
|
||||
- **Terminology debate:** "skeleton" bad, prefer "grammar" or "schema"
|
||||
- **Grammar-Constrained Decoding (GCD)** research — position Dervish as bridging structured output generation
|
||||
- Final task: deep research into GCD + neuro-symbolic generation literature
|
||||
|
||||
### Key Decisions
|
||||
- **TreeSitter AST → Dervish pipeline** is the core architecture
|
||||
- **No per-language heuristics** — use TreeSitter's universal node types and community tag queries
|
||||
- **Single-pass frequency analysis** filters noise
|
||||
- **1.2 (documents) dropped** — focus on code repos + structured data
|
||||
- **Features isolated** before integration
|
||||
- **This is novel research** — no prior work on grammar inference from TreeSitter ASTs
|
||||
|
||||
---
|
||||
|
||||
## Chat 4: "Dervish: Grammar Inference for LLM Agents" (27 turns)
|
||||
**Model:** 3.1 Pro | **Link:** share/a5ff288e0fdf
|
||||
|
||||
### Relationship to Chat 3
|
||||
- **IDENTICAL to Chat 3 for Turns 1-27** (same conversation, same user messages, same AI responses)
|
||||
- **Ends at Turn 27** — Chat 3 continues with 5 additional turns (28-32)
|
||||
- Chat 4 = the "short branch" of the conversation
|
||||
|
||||
---
|
||||
|
||||
## Divergence Map
|
||||
|
||||
```
|
||||
Chat 1 (32 turns) — LLMs/Agenten/Schema
|
||||
─────────────────────────────────
|
||||
Separate conversation, different focus (MCP, YAML-native, PDF algorithms)
|
||||
|
||||
Chat 2 (14 turns) — Regex theory
|
||||
─────────────────────────────────
|
||||
Separate conversation, different focus (regex theory, embabel, GOAP)
|
||||
|
||||
Chat 3 (32 turns) — Dervish: Grammar Inference
|
||||
Chat 4 (27 turns) — Dervish: Grammar Inference
|
||||
│
|
||||
├── Turns 1-5: README polish (MDL wording, generative schema section)
|
||||
├── Turns 6-10: Architecture (DomainRouter, MDL ensemble, TreeSitter idea)
|
||||
├── Turns 11-17: TreeSitter deep dive (language-agnostic, prior art, roadmapping)
|
||||
├── Turns 18-20: Formal roadmap, 1.2 dropped
|
||||
├── Turns 21-27: Implementation design (tags queries, frequency analysis, Spring Boot example)
|
||||
│
|
||||
└── Turn 27: "Will it find genuine insights?" (SAME question in both)
|
||||
├── Chat 3 continues (Turns 28-32)
|
||||
│ ├── 28: BEX in LLM training (structural tokenization, SoT)
|
||||
│ ├── 29: Paper search on GCD approaches
|
||||
│ ├── 30: arXiv paper title proposals
|
||||
│ ├── 31: Terminology debate (grammar vs skeleton)
|
||||
│ └── 32: GCD research assignment
|
||||
│
|
||||
└── Chat 4 ENDS at Turn 27
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Planned Feature Roadmap (from Chats 3-4)
|
||||
|
||||
### Phase 1.0: Code Repository Analysis (TreeSitter AST → Dervish)
|
||||
1. **Language detection** (file extension, MIME type)
|
||||
2. **TreeSitter parsing** with language-specific grammar
|
||||
3. **UNIVERSAL_STRUCTURE_QUERY** (single query for all languages):
|
||||
- `(call_expression) @call`
|
||||
- `(annotation) @meta` / `(decorator) @meta`
|
||||
- `(try_statement) @block` / `(catch_clause) @block`
|
||||
- Alternatively: load community `tags.scm` for each language
|
||||
4. **Single-pass frequency analysis** — count symbol frequency across files, filter outliers below threshold
|
||||
5. **Dervish inference** on cleaned sequences → compact grammar
|
||||
6. **Output:** ~60-200 token rule representing codebase conventions
|
||||
|
||||
### Phase 1.1: Structured Data (existing feature, enhance)
|
||||
- YAML/XML/JSON sequences → grammar inference
|
||||
- Already partially implemented in Dervish
|
||||
|
||||
### Phase 1.2: Markdown/Document AST (DROPPED)
|
||||
- Too fuzzy, documents vary too much
|
||||
- Not a priority
|
||||
|
||||
### Phase 2.0: Cross-File Conventions (future)
|
||||
- Class hierarchies, test patterns, file-level relationships
|
||||
- Beyond single-file AST analysis
|
||||
|
||||
### Broader Research (Chat 3 extra turns)
|
||||
- **Structural tokenization** — replace BPE with grammar-guided tokenization
|
||||
- **Grammar-Constrained Decoding (GCD)** — position Dervish in the GCD ecosystem
|
||||
- **arXiv paper** — "DERVISH: Neuro-Symbolic Code Generation via MDL-Optimized Structural Grammars"
|
||||
- **Literature review** — deep search on GCD + neuro-symbolic generation
|
||||
|
||||
---
|
||||
|
||||
## Repository State
|
||||
- **Main repo:** grammar-inference-engine (git submodule at `projects/grammar-inference-engine/`)
|
||||
- **Branch:** `feature/dervish-2`
|
||||
- **Remote:** `origin → https://forgejo.corentic.eu/tobi/grammar-inference-engine`
|
||||
- **Current code:** BEX algorithms (CRX, iDRegEx), MCP server, basic ORES/SORE/CHARE types
|
||||
- **Need 5th Gemini share link** — user mentioned 5 chats, we only have 4
|
||||
81
references/tags-queries/ANALYSIS.md
Normal file
81
references/tags-queries/ANALYSIS.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# TreeSitter Tag Queries for Dervish
|
||||
|
||||
## What We Got
|
||||
|
||||
Queries from 8 official TreeSitter language packages (`references/tags-queries/*.scm`):
|
||||
|
||||
| Language | TAGS_QUERY | HIGHLIGHTS_QUERY | Tag nodes | Dervish-relevant |
|
||||
|----------|-----------|-----------------|-----------|-----------------|
|
||||
| Java | ✅ 20 lines | ✅ 60 lines | `class_declaration`, `method_declaration`, `method_invocation`, `annotation` | `annotation`, `marker_annotation`, `method_invocation` |
|
||||
| Python | ✅ 14 lines | ✅ 50 lines | `class_definition`, `function_definition`, `call`, `assignment` | `call`, `decorator` |
|
||||
| TypeScript | ✅ 23 lines | ✅ 30 lines | `function_signature`, `interface_declaration`, `method_signature` | (none directly) |
|
||||
| Go | ✅ 42 lines | ✅ 55 lines | `function_declaration`, `call_expression`, `method_declaration` | `call_expression` |
|
||||
| C# | empty | ✅ | — | — |
|
||||
| C++ | ✅ 15 lines | ✅ 40 lines | `struct_specifier`, `function_declarator` | `call_expression` |
|
||||
| Rust | ✅ 60 lines | ✅ 80 lines | `struct_item`, `function_item`, `call_expression`, `macro_invocation` | `call_expression`, `macro_invocation` |
|
||||
| YAML | — | ✅ 30 lines | — | — |
|
||||
|
||||
## Key Finding
|
||||
|
||||
**Community TAGS_QUERIES are for code navigation (Go to Definition), NOT for Dervish.**
|
||||
|
||||
They capture:
|
||||
- `@definition.class` — where classes are declared
|
||||
- `@definition.method` — where methods are declared
|
||||
- `@reference.call` — where methods are CALLED (this IS useful for us)
|
||||
|
||||
## Dervish Needs Behavioral Nodes
|
||||
|
||||
From actual code parsing tests, these node types exist across languages:
|
||||
|
||||
| Pattern | Java | Python | Go | Rust |
|
||||
|---------|------|--------|----|------|
|
||||
| Function calls | `method_invocation` | `call` | `call_expression` | `call_expression` |
|
||||
| Annotations | `annotation`, `marker_annotation` | `decorator` | — | `macro_invocation` |
|
||||
| Conditionals | `if_statement` | `if_statement` | `if_statement` | `if_expression` |
|
||||
| Error throwing | `throw_statement` | `raise_statement` | `return`-based | `return`-based |
|
||||
| Error handling | `try_statement` | `try_statement` | — | — |
|
||||
| Variable decl | `local_variable_declaration` | `assignment` | `short_var_declaration` | `let_declaration` |
|
||||
| Returns | `return_statement` | `return_statement` | `return_statement` | `return` (no _statement) |
|
||||
|
||||
## Universal Query Approach (Confirmed Viable)
|
||||
|
||||
TreeSitter silently ignores non-existent node types in queries. One query for all languages:
|
||||
|
||||
```scheme
|
||||
; === CALLS (all imperative langs) ===
|
||||
(call_expression) @call ; Go, Rust, C++, TS/JS
|
||||
(call) @call ; Python
|
||||
(method_invocation) @call ; Java
|
||||
|
||||
; === METADATA ===
|
||||
(annotation) @meta ; Java, C#, Kotlin
|
||||
(marker_annotation) @meta ; Java
|
||||
(decorator) @meta ; Python
|
||||
(macro_invocation) @meta ; Rust macros
|
||||
|
||||
; === CONTROL FLOW ===
|
||||
(if_statement) @branch ; Java, Go, Python, C++
|
||||
(if_expression) @branch ; Rust
|
||||
(throw_statement) @throw ; Java, C++, C#
|
||||
(raise_statement) @throw ; Python
|
||||
(try_statement) @try ; Java, Python, C++, TS/JS
|
||||
(catch_clause) @catch ; Java, C++, TS/JS
|
||||
(except_clause) @catch ; Python
|
||||
(return_statement) @return ; Java, Go, Python, C++
|
||||
|
||||
; === RESOURCE MANAGEMENT ===
|
||||
(with_statement) @resource ; Python, TS/JS
|
||||
(defer_statement) @resource ; Go
|
||||
|
||||
; === OBJECT CREATION ===
|
||||
(object_creation_expression) @new ; Java, C++
|
||||
(new_expression) @new ; TS/JS
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Implement TreeSitter parser wrapper that loads correct grammar by file extension
|
||||
2. Apply the universal query to extract symbol sequences
|
||||
3. Add single-pass frequency analysis to filter noise
|
||||
4. Feed cleaned sequences into Dervish (CRX/iDRegEx)
|
||||
70
references/tags-queries/cpp_highlights_query.scm
Normal file
70
references/tags-queries/cpp_highlights_query.scm
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
; Functions
|
||||
|
||||
(call_expression
|
||||
function: (qualified_identifier
|
||||
name: (identifier) @function))
|
||||
|
||||
(template_function
|
||||
name: (identifier) @function)
|
||||
|
||||
(template_method
|
||||
name: (field_identifier) @function)
|
||||
|
||||
(template_function
|
||||
name: (identifier) @function)
|
||||
|
||||
(function_declarator
|
||||
declarator: (qualified_identifier
|
||||
name: (identifier) @function))
|
||||
|
||||
(function_declarator
|
||||
declarator: (field_identifier) @function)
|
||||
|
||||
; Types
|
||||
|
||||
((namespace_identifier) @type
|
||||
(#match? @type "^[A-Z]"))
|
||||
|
||||
(auto) @type
|
||||
|
||||
; Constants
|
||||
|
||||
(this) @variable.builtin
|
||||
(null "nullptr" @constant)
|
||||
|
||||
; Keywords
|
||||
|
||||
[
|
||||
"catch"
|
||||
"class"
|
||||
"co_await"
|
||||
"co_return"
|
||||
"co_yield"
|
||||
"constexpr"
|
||||
"constinit"
|
||||
"consteval"
|
||||
"delete"
|
||||
"explicit"
|
||||
"final"
|
||||
"friend"
|
||||
"mutable"
|
||||
"namespace"
|
||||
"noexcept"
|
||||
"new"
|
||||
"override"
|
||||
"private"
|
||||
"protected"
|
||||
"public"
|
||||
"template"
|
||||
"throw"
|
||||
"try"
|
||||
"typename"
|
||||
"using"
|
||||
"concept"
|
||||
"requires"
|
||||
"virtual"
|
||||
] @keyword
|
||||
|
||||
; Strings
|
||||
|
||||
(raw_string_literal) @string
|
||||
3
references/tags-queries/cpp_injections_query.scm
Normal file
3
references/tags-queries/cpp_injections_query.scm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(raw_string_literal
|
||||
delimiter: (raw_string_delimiter) @injection.language
|
||||
(raw_string_content) @injection.content)
|
||||
15
references/tags-queries/cpp_tags_query.scm
Normal file
15
references/tags-queries/cpp_tags_query.scm
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
(struct_specifier name: (type_identifier) @name body:(_)) @definition.class
|
||||
|
||||
(declaration type: (union_specifier name: (type_identifier) @name)) @definition.class
|
||||
|
||||
(function_declarator declarator: (identifier) @name) @definition.function
|
||||
|
||||
(function_declarator declarator: (field_identifier) @name) @definition.function
|
||||
|
||||
(function_declarator declarator: (qualified_identifier scope: (namespace_identifier) @local.scope name: (identifier) @name)) @definition.method
|
||||
|
||||
(type_definition declarator: (type_identifier) @name) @definition.type
|
||||
|
||||
(enum_specifier name: (type_identifier) @name) @definition.type
|
||||
|
||||
(class_specifier name: (type_identifier) @name) @definition.class
|
||||
123
references/tags-queries/go_highlights_query.scm
Normal file
123
references/tags-queries/go_highlights_query.scm
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
; Function calls
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function)
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function.builtin
|
||||
(#match? @function.builtin "^(append|cap|close|complex|copy|delete|imag|len|make|new|panic|print|println|real|recover)$"))
|
||||
|
||||
(call_expression
|
||||
function: (selector_expression
|
||||
field: (field_identifier) @function.method))
|
||||
|
||||
; Function definitions
|
||||
|
||||
(function_declaration
|
||||
name: (identifier) @function)
|
||||
|
||||
(method_declaration
|
||||
name: (field_identifier) @function.method)
|
||||
|
||||
; Identifiers
|
||||
|
||||
(type_identifier) @type
|
||||
(field_identifier) @property
|
||||
(identifier) @variable
|
||||
|
||||
; Operators
|
||||
|
||||
[
|
||||
"--"
|
||||
"-"
|
||||
"-="
|
||||
":="
|
||||
"!"
|
||||
"!="
|
||||
"..."
|
||||
"*"
|
||||
"*"
|
||||
"*="
|
||||
"/"
|
||||
"/="
|
||||
"&"
|
||||
"&&"
|
||||
"&="
|
||||
"%"
|
||||
"%="
|
||||
"^"
|
||||
"^="
|
||||
"+"
|
||||
"++"
|
||||
"+="
|
||||
"<-"
|
||||
"<"
|
||||
"<<"
|
||||
"<<="
|
||||
"<="
|
||||
"="
|
||||
"=="
|
||||
">"
|
||||
">="
|
||||
">>"
|
||||
">>="
|
||||
"|"
|
||||
"|="
|
||||
"||"
|
||||
"~"
|
||||
] @operator
|
||||
|
||||
; Keywords
|
||||
|
||||
[
|
||||
"break"
|
||||
"case"
|
||||
"chan"
|
||||
"const"
|
||||
"continue"
|
||||
"default"
|
||||
"defer"
|
||||
"else"
|
||||
"fallthrough"
|
||||
"for"
|
||||
"func"
|
||||
"go"
|
||||
"goto"
|
||||
"if"
|
||||
"import"
|
||||
"interface"
|
||||
"map"
|
||||
"package"
|
||||
"range"
|
||||
"return"
|
||||
"select"
|
||||
"struct"
|
||||
"switch"
|
||||
"type"
|
||||
"var"
|
||||
] @keyword
|
||||
|
||||
; Literals
|
||||
|
||||
[
|
||||
(interpreted_string_literal)
|
||||
(raw_string_literal)
|
||||
(rune_literal)
|
||||
] @string
|
||||
|
||||
(escape_sequence) @escape
|
||||
|
||||
[
|
||||
(int_literal)
|
||||
(float_literal)
|
||||
(imaginary_literal)
|
||||
] @number
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
(nil)
|
||||
(iota)
|
||||
] @constant.builtin
|
||||
|
||||
(comment) @comment
|
||||
42
references/tags-queries/go_tags_query.scm
Normal file
42
references/tags-queries/go_tags_query.scm
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
(
|
||||
(comment)* @doc
|
||||
.
|
||||
(function_declaration
|
||||
name: (identifier) @name) @definition.function
|
||||
(#strip! @doc "^//\\s*")
|
||||
(#set-adjacent! @doc @definition.function)
|
||||
)
|
||||
|
||||
(
|
||||
(comment)* @doc
|
||||
.
|
||||
(method_declaration
|
||||
name: (field_identifier) @name) @definition.method
|
||||
(#strip! @doc "^//\\s*")
|
||||
(#set-adjacent! @doc @definition.method)
|
||||
)
|
||||
|
||||
(call_expression
|
||||
function: [
|
||||
(identifier) @name
|
||||
(parenthesized_expression (identifier) @name)
|
||||
(selector_expression field: (field_identifier) @name)
|
||||
(parenthesized_expression (selector_expression field: (field_identifier) @name))
|
||||
]) @reference.call
|
||||
|
||||
(type_spec
|
||||
name: (type_identifier) @name) @definition.type
|
||||
|
||||
(type_identifier) @name @reference.type
|
||||
|
||||
(package_clause "package" (package_identifier) @name)
|
||||
|
||||
(type_declaration (type_spec name: (type_identifier) @name type: (interface_type)))
|
||||
|
||||
(type_declaration (type_spec name: (type_identifier) @name type: (struct_type)))
|
||||
|
||||
(import_declaration (import_spec) @name)
|
||||
|
||||
(var_declaration (var_spec name: (identifier) @name))
|
||||
|
||||
(const_declaration (const_spec name: (identifier) @name))
|
||||
149
references/tags-queries/java_highlights_query.scm
Normal file
149
references/tags-queries/java_highlights_query.scm
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
; Variables
|
||||
|
||||
(identifier) @variable
|
||||
|
||||
; Methods
|
||||
|
||||
(method_declaration
|
||||
name: (identifier) @function.method)
|
||||
(method_invocation
|
||||
name: (identifier) @function.method)
|
||||
(super) @function.builtin
|
||||
|
||||
; Annotations
|
||||
|
||||
(annotation
|
||||
name: (identifier) @attribute)
|
||||
(marker_annotation
|
||||
name: (identifier) @attribute)
|
||||
|
||||
"@" @operator
|
||||
|
||||
; Types
|
||||
|
||||
(type_identifier) @type
|
||||
|
||||
(interface_declaration
|
||||
name: (identifier) @type)
|
||||
(class_declaration
|
||||
name: (identifier) @type)
|
||||
(enum_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
((field_access
|
||||
object: (identifier) @type)
|
||||
(#match? @type "^[A-Z]"))
|
||||
((scoped_identifier
|
||||
scope: (identifier) @type)
|
||||
(#match? @type "^[A-Z]"))
|
||||
((method_invocation
|
||||
object: (identifier) @type)
|
||||
(#match? @type "^[A-Z]"))
|
||||
((method_reference
|
||||
. (identifier) @type)
|
||||
(#match? @type "^[A-Z]"))
|
||||
|
||||
(constructor_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
[
|
||||
(boolean_type)
|
||||
(integral_type)
|
||||
(floating_point_type)
|
||||
(floating_point_type)
|
||||
(void_type)
|
||||
] @type.builtin
|
||||
|
||||
; Constants
|
||||
|
||||
((identifier) @constant
|
||||
(#match? @constant "^_*[A-Z][A-Z\\d_]+$"))
|
||||
|
||||
; Builtins
|
||||
|
||||
(this) @variable.builtin
|
||||
|
||||
; Literals
|
||||
|
||||
[
|
||||
(hex_integer_literal)
|
||||
(decimal_integer_literal)
|
||||
(octal_integer_literal)
|
||||
(decimal_floating_point_literal)
|
||||
(hex_floating_point_literal)
|
||||
] @number
|
||||
|
||||
[
|
||||
(character_literal)
|
||||
(string_literal)
|
||||
] @string
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
(null_literal)
|
||||
] @constant.builtin
|
||||
|
||||
[
|
||||
(line_comment)
|
||||
(block_comment)
|
||||
] @comment
|
||||
|
||||
; Keywords
|
||||
|
||||
[
|
||||
"abstract"
|
||||
"assert"
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"class"
|
||||
"continue"
|
||||
"default"
|
||||
"do"
|
||||
"else"
|
||||
"enum"
|
||||
"exports"
|
||||
"extends"
|
||||
"final"
|
||||
"finally"
|
||||
"for"
|
||||
"if"
|
||||
"implements"
|
||||
"import"
|
||||
"instanceof"
|
||||
"interface"
|
||||
"module"
|
||||
"native"
|
||||
"new"
|
||||
"non-sealed"
|
||||
"open"
|
||||
"opens"
|
||||
"package"
|
||||
"permits"
|
||||
"private"
|
||||
"protected"
|
||||
"provides"
|
||||
"public"
|
||||
"requires"
|
||||
"record"
|
||||
"return"
|
||||
"sealed"
|
||||
"static"
|
||||
"strictfp"
|
||||
"switch"
|
||||
"synchronized"
|
||||
"throw"
|
||||
"throws"
|
||||
"to"
|
||||
"transient"
|
||||
"transitive"
|
||||
"try"
|
||||
"uses"
|
||||
"volatile"
|
||||
"when"
|
||||
"while"
|
||||
"with"
|
||||
"yield"
|
||||
] @keyword
|
||||
20
references/tags-queries/java_tags_query.scm
Normal file
20
references/tags-queries/java_tags_query.scm
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
(class_declaration
|
||||
name: (identifier) @name) @definition.class
|
||||
|
||||
(method_declaration
|
||||
name: (identifier) @name) @definition.method
|
||||
|
||||
(method_invocation
|
||||
name: (identifier) @name
|
||||
arguments: (argument_list) @reference.call)
|
||||
|
||||
(interface_declaration
|
||||
name: (identifier) @name) @definition.interface
|
||||
|
||||
(type_list
|
||||
(type_identifier) @name) @reference.implementation
|
||||
|
||||
(object_creation_expression
|
||||
type: (type_identifier) @name) @reference.class
|
||||
|
||||
(superclass (type_identifier) @name) @reference.class
|
||||
137
references/tags-queries/python_highlights_query.scm
Normal file
137
references/tags-queries/python_highlights_query.scm
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
; Identifier naming conventions
|
||||
|
||||
(identifier) @variable
|
||||
|
||||
((identifier) @constructor
|
||||
(#match? @constructor "^[A-Z]"))
|
||||
|
||||
((identifier) @constant
|
||||
(#match? @constant "^[A-Z][A-Z_]*$"))
|
||||
|
||||
; Function calls
|
||||
|
||||
(decorator) @function
|
||||
(decorator
|
||||
(identifier) @function)
|
||||
|
||||
(call
|
||||
function: (attribute attribute: (identifier) @function.method))
|
||||
(call
|
||||
function: (identifier) @function)
|
||||
|
||||
; Builtin functions
|
||||
|
||||
((call
|
||||
function: (identifier) @function.builtin)
|
||||
(#match?
|
||||
@function.builtin
|
||||
"^(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dict|dir|divmod|enumerate|eval|exec|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|isinstance|issubclass|iter|len|list|locals|map|max|memoryview|min|next|object|oct|open|ord|pow|print|property|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|vars|zip|__import__)$"))
|
||||
|
||||
; Function definitions
|
||||
|
||||
(function_definition
|
||||
name: (identifier) @function)
|
||||
|
||||
(attribute attribute: (identifier) @property)
|
||||
(type (identifier) @type)
|
||||
|
||||
; Literals
|
||||
|
||||
[
|
||||
(none)
|
||||
(true)
|
||||
(false)
|
||||
] @constant.builtin
|
||||
|
||||
[
|
||||
(integer)
|
||||
(float)
|
||||
] @number
|
||||
|
||||
(comment) @comment
|
||||
(string) @string
|
||||
(escape_sequence) @escape
|
||||
|
||||
(interpolation
|
||||
"{" @punctuation.special
|
||||
"}" @punctuation.special) @embedded
|
||||
|
||||
[
|
||||
"-"
|
||||
"-="
|
||||
"!="
|
||||
"*"
|
||||
"**"
|
||||
"**="
|
||||
"*="
|
||||
"/"
|
||||
"//"
|
||||
"//="
|
||||
"/="
|
||||
"&"
|
||||
"&="
|
||||
"%"
|
||||
"%="
|
||||
"^"
|
||||
"^="
|
||||
"+"
|
||||
"->"
|
||||
"+="
|
||||
"<"
|
||||
"<<"
|
||||
"<<="
|
||||
"<="
|
||||
"<>"
|
||||
"="
|
||||
":="
|
||||
"=="
|
||||
">"
|
||||
">="
|
||||
">>"
|
||||
">>="
|
||||
"|"
|
||||
"|="
|
||||
"~"
|
||||
"@="
|
||||
"and"
|
||||
"in"
|
||||
"is"
|
||||
"not"
|
||||
"or"
|
||||
"is not"
|
||||
"not in"
|
||||
] @operator
|
||||
|
||||
[
|
||||
"as"
|
||||
"assert"
|
||||
"async"
|
||||
"await"
|
||||
"break"
|
||||
"class"
|
||||
"continue"
|
||||
"def"
|
||||
"del"
|
||||
"elif"
|
||||
"else"
|
||||
"except"
|
||||
"exec"
|
||||
"finally"
|
||||
"for"
|
||||
"from"
|
||||
"global"
|
||||
"if"
|
||||
"import"
|
||||
"lambda"
|
||||
"nonlocal"
|
||||
"pass"
|
||||
"print"
|
||||
"raise"
|
||||
"return"
|
||||
"try"
|
||||
"while"
|
||||
"with"
|
||||
"yield"
|
||||
"match"
|
||||
"case"
|
||||
] @keyword
|
||||
14
references/tags-queries/python_tags_query.scm
Normal file
14
references/tags-queries/python_tags_query.scm
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
(module (expression_statement (assignment left: (identifier) @name) @definition.constant))
|
||||
|
||||
(class_definition
|
||||
name: (identifier) @name) @definition.class
|
||||
|
||||
(function_definition
|
||||
name: (identifier) @name) @definition.function
|
||||
|
||||
(call
|
||||
function: [
|
||||
(identifier) @name
|
||||
(attribute
|
||||
attribute: (identifier) @name)
|
||||
]) @reference.call
|
||||
161
references/tags-queries/rust_highlights_query.scm
Normal file
161
references/tags-queries/rust_highlights_query.scm
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
; Identifiers
|
||||
|
||||
(type_identifier) @type
|
||||
(primitive_type) @type.builtin
|
||||
(field_identifier) @property
|
||||
|
||||
; Identifier conventions
|
||||
|
||||
; Assume all-caps names are constants
|
||||
((identifier) @constant
|
||||
(#match? @constant "^[A-Z][A-Z\\d_]+$'"))
|
||||
|
||||
; Assume uppercase names are enum constructors
|
||||
((identifier) @constructor
|
||||
(#match? @constructor "^[A-Z]"))
|
||||
|
||||
; Assume that uppercase names in paths are types
|
||||
((scoped_identifier
|
||||
path: (identifier) @type)
|
||||
(#match? @type "^[A-Z]"))
|
||||
((scoped_identifier
|
||||
path: (scoped_identifier
|
||||
name: (identifier) @type))
|
||||
(#match? @type "^[A-Z]"))
|
||||
((scoped_type_identifier
|
||||
path: (identifier) @type)
|
||||
(#match? @type "^[A-Z]"))
|
||||
((scoped_type_identifier
|
||||
path: (scoped_identifier
|
||||
name: (identifier) @type))
|
||||
(#match? @type "^[A-Z]"))
|
||||
|
||||
; Assume all qualified names in struct patterns are enum constructors. (They're
|
||||
; either that, or struct names; highlighting both as constructors seems to be
|
||||
; the less glaring choice of error, visually.)
|
||||
(struct_pattern
|
||||
type: (scoped_type_identifier
|
||||
name: (type_identifier) @constructor))
|
||||
|
||||
; Function calls
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function)
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function.method))
|
||||
(call_expression
|
||||
function: (scoped_identifier
|
||||
"::"
|
||||
name: (identifier) @function))
|
||||
|
||||
(generic_function
|
||||
function: (identifier) @function)
|
||||
(generic_function
|
||||
function: (scoped_identifier
|
||||
name: (identifier) @function))
|
||||
(generic_function
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function.method))
|
||||
|
||||
(macro_invocation
|
||||
macro: (identifier) @function.macro
|
||||
"!" @function.macro)
|
||||
|
||||
; Function definitions
|
||||
|
||||
(function_item (identifier) @function)
|
||||
(function_signature_item (identifier) @function)
|
||||
|
||||
(line_comment) @comment
|
||||
(block_comment) @comment
|
||||
|
||||
(line_comment (doc_comment)) @comment.documentation
|
||||
(block_comment (doc_comment)) @comment.documentation
|
||||
|
||||
"(" @punctuation.bracket
|
||||
")" @punctuation.bracket
|
||||
"[" @punctuation.bracket
|
||||
"]" @punctuation.bracket
|
||||
"{" @punctuation.bracket
|
||||
"}" @punctuation.bracket
|
||||
|
||||
(type_arguments
|
||||
"<" @punctuation.bracket
|
||||
">" @punctuation.bracket)
|
||||
(type_parameters
|
||||
"<" @punctuation.bracket
|
||||
">" @punctuation.bracket)
|
||||
|
||||
"::" @punctuation.delimiter
|
||||
":" @punctuation.delimiter
|
||||
"." @punctuation.delimiter
|
||||
"," @punctuation.delimiter
|
||||
";" @punctuation.delimiter
|
||||
|
||||
(parameter (identifier) @variable.parameter)
|
||||
|
||||
(lifetime (identifier) @label)
|
||||
|
||||
"as" @keyword
|
||||
"async" @keyword
|
||||
"await" @keyword
|
||||
"break" @keyword
|
||||
"const" @keyword
|
||||
"continue" @keyword
|
||||
"default" @keyword
|
||||
"dyn" @keyword
|
||||
"else" @keyword
|
||||
"enum" @keyword
|
||||
"extern" @keyword
|
||||
"fn" @keyword
|
||||
"for" @keyword
|
||||
"gen" @keyword
|
||||
"if" @keyword
|
||||
"impl" @keyword
|
||||
"in" @keyword
|
||||
"let" @keyword
|
||||
"loop" @keyword
|
||||
"macro_rules!" @keyword
|
||||
"match" @keyword
|
||||
"mod" @keyword
|
||||
"move" @keyword
|
||||
"pub" @keyword
|
||||
"raw" @keyword
|
||||
"ref" @keyword
|
||||
"return" @keyword
|
||||
"static" @keyword
|
||||
"struct" @keyword
|
||||
"trait" @keyword
|
||||
"type" @keyword
|
||||
"union" @keyword
|
||||
"unsafe" @keyword
|
||||
"use" @keyword
|
||||
"where" @keyword
|
||||
"while" @keyword
|
||||
"yield" @keyword
|
||||
(crate) @keyword
|
||||
(mutable_specifier) @keyword
|
||||
(use_list (self) @keyword)
|
||||
(scoped_use_list (self) @keyword)
|
||||
(scoped_identifier (self) @keyword)
|
||||
(super) @keyword
|
||||
|
||||
(self) @variable.builtin
|
||||
|
||||
(char_literal) @string
|
||||
(string_literal) @string
|
||||
(raw_string_literal) @string
|
||||
|
||||
(boolean_literal) @constant.builtin
|
||||
(integer_literal) @constant.builtin
|
||||
(float_literal) @constant.builtin
|
||||
|
||||
(escape_sequence) @escape
|
||||
|
||||
(attribute_item) @attribute
|
||||
(inner_attribute_item) @attribute
|
||||
|
||||
"*" @operator
|
||||
"&" @operator
|
||||
"'" @operator
|
||||
9
references/tags-queries/rust_injections_query.scm
Normal file
9
references/tags-queries/rust_injections_query.scm
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
((macro_invocation
|
||||
(token_tree) @injection.content)
|
||||
(#set! injection.language "rust")
|
||||
(#set! injection.include-children))
|
||||
|
||||
((macro_rule
|
||||
(token_tree) @injection.content)
|
||||
(#set! injection.language "rust")
|
||||
(#set! injection.include-children))
|
||||
60
references/tags-queries/rust_tags_query.scm
Normal file
60
references/tags-queries/rust_tags_query.scm
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
; ADT definitions
|
||||
|
||||
(struct_item
|
||||
name: (type_identifier) @name) @definition.class
|
||||
|
||||
(enum_item
|
||||
name: (type_identifier) @name) @definition.class
|
||||
|
||||
(union_item
|
||||
name: (type_identifier) @name) @definition.class
|
||||
|
||||
; type aliases
|
||||
|
||||
(type_item
|
||||
name: (type_identifier) @name) @definition.class
|
||||
|
||||
; method definitions
|
||||
|
||||
(declaration_list
|
||||
(function_item
|
||||
name: (identifier) @name) @definition.method)
|
||||
|
||||
; function definitions
|
||||
|
||||
(function_item
|
||||
name: (identifier) @name) @definition.function
|
||||
|
||||
; trait definitions
|
||||
(trait_item
|
||||
name: (type_identifier) @name) @definition.interface
|
||||
|
||||
; module definitions
|
||||
(mod_item
|
||||
name: (identifier) @name) @definition.module
|
||||
|
||||
; macro definitions
|
||||
|
||||
(macro_definition
|
||||
name: (identifier) @name) @definition.macro
|
||||
|
||||
; references
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @name) @reference.call
|
||||
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @name)) @reference.call
|
||||
|
||||
(macro_invocation
|
||||
macro: (identifier) @name) @reference.call
|
||||
|
||||
; implementations
|
||||
|
||||
(impl_item
|
||||
trait: (type_identifier) @name) @reference.implementation
|
||||
|
||||
(impl_item
|
||||
type: (type_identifier) @name
|
||||
!trait) @reference.implementation
|
||||
35
references/tags-queries/typescript_highlights_query.scm
Normal file
35
references/tags-queries/typescript_highlights_query.scm
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
; Types
|
||||
|
||||
(type_identifier) @type
|
||||
(predefined_type) @type.builtin
|
||||
|
||||
((identifier) @type
|
||||
(#match? @type "^[A-Z]"))
|
||||
|
||||
(type_arguments
|
||||
"<" @punctuation.bracket
|
||||
">" @punctuation.bracket)
|
||||
|
||||
; Variables
|
||||
|
||||
(required_parameter (identifier) @variable.parameter)
|
||||
(optional_parameter (identifier) @variable.parameter)
|
||||
|
||||
; Keywords
|
||||
|
||||
[ "abstract"
|
||||
"declare"
|
||||
"enum"
|
||||
"export"
|
||||
"implements"
|
||||
"interface"
|
||||
"keyof"
|
||||
"namespace"
|
||||
"private"
|
||||
"protected"
|
||||
"public"
|
||||
"type"
|
||||
"readonly"
|
||||
"override"
|
||||
"satisfies"
|
||||
] @keyword
|
||||
23
references/tags-queries/typescript_tags_query.scm
Normal file
23
references/tags-queries/typescript_tags_query.scm
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
(function_signature
|
||||
name: (identifier) @name) @definition.function
|
||||
|
||||
(method_signature
|
||||
name: (property_identifier) @name) @definition.method
|
||||
|
||||
(abstract_method_signature
|
||||
name: (property_identifier) @name) @definition.method
|
||||
|
||||
(abstract_class_declaration
|
||||
name: (type_identifier) @name) @definition.class
|
||||
|
||||
(module
|
||||
name: (identifier) @name) @definition.module
|
||||
|
||||
(interface_declaration
|
||||
name: (type_identifier) @name) @definition.interface
|
||||
|
||||
(type_annotation
|
||||
(type_identifier) @name) @reference.type
|
||||
|
||||
(new_expression
|
||||
constructor: (identifier) @name) @reference.class
|
||||
79
references/tags-queries/yaml_highlights_query.scm
Normal file
79
references/tags-queries/yaml_highlights_query.scm
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
(boolean_scalar) @boolean
|
||||
|
||||
(null_scalar) @constant.builtin
|
||||
|
||||
[
|
||||
(double_quote_scalar)
|
||||
(single_quote_scalar)
|
||||
(block_scalar)
|
||||
(string_scalar)
|
||||
] @string
|
||||
|
||||
[
|
||||
(integer_scalar)
|
||||
(float_scalar)
|
||||
] @number
|
||||
|
||||
(comment) @comment
|
||||
|
||||
[
|
||||
(anchor_name)
|
||||
(alias_name)
|
||||
] @label
|
||||
|
||||
(tag) @type
|
||||
|
||||
[
|
||||
(yaml_directive)
|
||||
(tag_directive)
|
||||
(reserved_directive)
|
||||
] @attribute
|
||||
|
||||
(block_mapping_pair
|
||||
key: (flow_node
|
||||
[
|
||||
(double_quote_scalar)
|
||||
(single_quote_scalar)
|
||||
] @property))
|
||||
|
||||
(block_mapping_pair
|
||||
key: (flow_node
|
||||
(plain_scalar
|
||||
(string_scalar) @property)))
|
||||
|
||||
(flow_mapping
|
||||
(_
|
||||
key: (flow_node
|
||||
[
|
||||
(double_quote_scalar)
|
||||
(single_quote_scalar)
|
||||
] @property)))
|
||||
|
||||
(flow_mapping
|
||||
(_
|
||||
key: (flow_node
|
||||
(plain_scalar
|
||||
(string_scalar) @property))))
|
||||
|
||||
[
|
||||
","
|
||||
"-"
|
||||
":"
|
||||
">"
|
||||
"?"
|
||||
"|"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"*"
|
||||
"&"
|
||||
"---"
|
||||
"..."
|
||||
] @punctuation.special
|
||||
Loading…
Add table
Reference in a new issue