From 4429280524a6b6e17d09be92e596439db2a7d938 Mon Sep 17 00:00:00 2001 From: Lazar Bulovan Date: Wed, 18 Feb 2026 10:25:35 +0100 Subject: [PATCH] docs(AGENTS.md): Update AGENTS.md The new AGENTS.md includes: - Reference project for testing - Build/test commands (LSP and tree-sitter) - Condensed project structure - Code style guidelines (module pattern, naming, imports, comments) - Parser module API convention - Cache management patterns - Error handling patterns (return pattern, pcall, validation) - Completion system documentation - Commands table --- AGENTS.md | 267 ++++++++++++++++++++++++++---------------------------- 1 file changed, 127 insertions(+), 140 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f187784..90141f4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,172 +1,159 @@ # AGENTS.md - SCL Language Server (Unified) -This is a unified Neovim/LazyVim plugin for Siemens SCL language support. - -## Project Overview - -The unified `scl_lsp` project provides: -- **LSP Server** - Language Server Protocol implementation -- **Linter** - Diagnostics and code validation -- **Formatter** - Document formatting -- **Syntax Highlighting** - Tree-sitter based -- **Auto-completion** - blink.cmp integration with smart context-aware completions -- **Auto-prefix** - Automatic `#` prefixing for variables -- **Workspace Scanning** - Automatic scanning of UDTs (.udt) and Global DBs (.db) +A unified Neovim/LazyVim plugin for Siemens SCL language support providing LSP, linting, formatting, syntax highlighting, and auto-completion. ## Reference Project for Testing -For testing and referencing SCL, UDT, DB, and XML files, use the reference project at: +For manual testing and SCL/UDT/DB file examples, use: ``` ~/dev/siemens/projects/scl_lang_support_lazyvim_ref_project ``` -This project contains example SCL files, UDT definitions, and global data blocks. +## Build/Lint/Test Commands + +### LSP Server +```bash +make start # Start LSP server +make test # Run LSP in test mode +lua src/main.lua --test # Direct test mode execution +``` + +### Tree-sitter Parser +```bash +tree-sitter generate # Generate parser from grammar.js +tree-sitter test # Run tree-sitter tests +tree-sitter parse # Parse a single SCL file +``` ## Project Structure ``` scl_lsp/ -├── src/ # LSP server implementation -│ ├── main.lua # Entry point, protocol handling -│ ├── parser.lua # Regex-based SCL parser -│ ├── treesitter.lua # Tree-sitter integration -│ ├── diagnostics.lua # Linter diagnostics -│ ├── formatter.lua # Document formatter -│ ├── json.lua # JSON encoder/decoder -│ └── plc_json.lua # External UDT loading -├── lua/ -│ ├── scl/ # Neovim plugin modules -│ │ ├── init.lua # Main plugin setup -│ │ ├── auto_prefix.lua # Auto-prefix variables with # -│ │ ├── blink_cmp_source.lua # blink.cmp completion source -│ │ ├── db_parser.lua # Global DB (.db) parser -│ │ ├── fb_parser.lua # Function Block parser -│ │ ├── udt_parser.lua # UDT (.udt) parser -│ │ ├── variables.lua # Local variable extraction -│ │ ├── workspace_types.lua # Workspace scanning (UDTs, DBs, FBs) -│ │ └── multiline_params.lua # Multiline parameter filling -│ └── scl_lsp/ -│ ├── init.lua # Unified plugin setup -│ └── plugins/ -│ └── scl.lua # LazyVim plugin spec -├── queries/ # Tree-sitter queries -│ ├── highlights.scm -│ ├── indents.scm -│ ├── folds.scm -│ ├── locals.scm -│ └── tags.scm -├── grammar.js # Tree-sitter grammar -└── tree-sitter.json # Parser config +├── src/ # Standalone LSP server (uses dofile) +│ ├── main.lua # Entry point, JSON-RPC protocol +│ ├── parser.lua # Regex-based SCL parser +│ ├── diagnostics.lua # Linter diagnostics +│ └── formatter.lua # Document formatter +├── lua/scl/ # Neovim plugin modules (uses require) +│ ├── blink_cmp_source.lua # blink.cmp completion source +│ ├── udt_parser.lua # UDT (.udt) parser +│ ├── db_parser.lua # Global DB (.db) parser +│ ├── fb_parser.lua # Function Block parser +│ ├── variables.lua # Local variable extraction +│ ├── workspace_types.lua # Workspace scanning +│ └── multiline_params.lua # FB parameter filling +└── queries/ # Tree-sitter queries ``` -## Key Modules +## Code Style Guidelines -### Parser Modules +### Module Pattern +All modules follow the standard Lua module pattern: +```lua +-- Module description comment at top +local M = {} -| Module | Purpose | -|--------|---------| -| `udt_parser.lua` | Parses `.udt` files to extract TYPE definitions and STRUCT members | -| `db_parser.lua` | Parses `.db` files to extract DATA_BLOCK definitions and VAR members | -| `fb_parser.lua` | Parses `.scl` files to extract FUNCTION_BLOCK and FUNCTION interfaces | -| `variables.lua` | Extracts local variables from VAR sections | +-- Private module-level cache +local cache = {} -### Completion System +function M.public_function() +end -| Module | Purpose | -|--------|---------| -| `blink_cmp_source.lua` | Main completion source for blink.cmp. Handles: | -| | - Local variables (with # prefix after BEGIN) | -| | - UDT types (quoted: "MyUDT") | -| | - UDT members (after dot: #var.member) | -| | - Global DB names (available in general completion) | -| | - Global DB members (after quote+dot: "DBName".member) | -| | - FB parameter completions (inside FB calls) | -| | - Basic SCL types | +local function private_function() +end -### Workspace Scanner +return M +``` -| Module | Purpose | -|--------|---------| -| `workspace_types.lua` | Scans project for: | -| | - `.udt` files (UDT types) | -| | - `.db` files (Global Data Blocks) | -| | - `.scl` files (Function Blocks/Functions) | -| | - Uses project markers to find project root | +### Naming Conventions +- Functions/variables: `snake_case` (e.g., `get_udt_members`, `var_types`) +- Constants: `UPPER_SNAKE_CASE` (e.g., `PROJECT_MARKERS`) +- Private functions: declare as `local function` before public functions +- Boolean variables: prefix with `is_`, `has_` (e.g., `is_udt`, `has_members`) -## Auto-Completion Features +### Imports +- `lua/scl/` modules: use `require("scl.module_name")` +- `src/` modules: use `dofile(script_path .. "/module.lua")` +- External dependencies: wrap in `pcall()` for safety + +### Comments +- Every file must have a header comment explaining its purpose +- All functions should have a brief comment describing what they do +- Complex logic requires inline comments explaining the "why" +- Non-obvious regex patterns must have explanatory comments + +### Parser Module API Convention +All parser modules (udt_parser, db_parser, fb_parser) must implement: +- `parse_*_file(filepath)` - Parse a file and cache result +- `parse_*_content(content, filename)` - Parse string content +- `get_*(name)` - Get single cached item by name +- `get_all_*_names()` - Return list of all cached names +- `get_*_members(name)` - Get members/fields for a type +- `is_*_type(name)` - Check if name is a known type +- `clear_cache()` - Clear all cached data +- `get_cache_count()` - Return number of cached items + +### Cache Management +- Use module-level local tables for caching: `local cache = {}` +- Clear caches by iterating and setting to nil, not by reassigning: +```lua +function M.clear_cache() + for k in pairs(cache) do + cache[k] = nil + end +end +``` + +## Error Handling + +### Return Pattern +Functions that can fail return `nil, "error message"`: +```lua +function M.parse_file(filepath) + local file = io.open(filepath, "r") + if not file then + return nil, "Cannot open file: " .. filepath + end + -- ... parse logic + return result +end +``` + +### External Dependencies +Always wrap external requires in `pcall()`: +```lua +local ok, module = pcall(require, "some_module") +if ok and module then + module.do_something() +end +``` + +### Validation +Check required parameters early and return sensible defaults: +```lua +function M.get_members(type_name) + if not type_name then + return {} + end + -- ... continue +end +``` + +## Completion System ### Trigger Characters - `#` - Local variables (after BEGIN) - `.` - Member access (UDT/DB members) -- `"` - Global DB names and member access -- `(` - FB/Function parameter completion -- ` ` (space) - General completion (variables, UDTs, DBs, types) +- `"` - Global DB names +- `(` - FB/Function parameters +- ` ` (space) - General completion -### Completion Contexts - -1. **Variable Completion** (after space or at start) - - Local variables from VAR sections - - UDT type names (quoted) - - Global DB names (wrapped in quotes automatically) - - Basic SCL types - -2. **Member Completion** (after `.`) - - UDT members (resolves through nested types) - - DB members (resolves through nested types) - - Supports multi-level nesting: `#var.member.submember` - -3. **Global DB Completion** - - Type `"` then select DB → shows DB members - - Or type space → select DB from list → automatically wrapped in quotes - -4. **FB Parameter Completion** (after `fbName(`) - - Input parameters (`:=`) - - InOut parameters (`:=`) - - Output parameters (`=>`) - -### Special Features - -- **Auto-Prefix**: Adds `#` to variables automatically after BEGIN -- **Quote Wrapping**: DB names selected from general completion are automatically wrapped in quotes -- **Nested Resolution**: Multi-level member path resolution for both UDTs and DBs - -## Development - -### Running the LSP Server - -```bash -# Direct execution -lua src/main.lua - -# Via shell script -./scl_lsp.sh - -# With test mode -lua src/main.lua --test +### Active Portion Detection +When matching patterns in completion, use the "active portion" of the line (after the last operator) to correctly handle expressions like: +```scl +"DB1".member := "DB2".member ``` - -### Testing - -```bash -# Tree-sitter parser -npm test -tree-sitter generate - -# Test file parsing -tree-sitter parse corpus/test/main_org_block.scl -``` - -### Key Patterns - -- **LSP Protocol**: JSON-RPC over stdin/stdout in `src/main.lua` -- **Diagnostics**: `src/diagnostics.lua` - validates SCL code -- **Formatter**: `src/formatter.lua` - formats SCL documents -- **Syntax**: Tree-sitter queries in `queries/` -- **Plugin Setup**: `lua/scl_lsp/init.lua` - LazyVim integration - -## Configuration - -See `README.md` for configuration options and usage. +Find the last `:=`, `=>`, `=`, `<>`, `>=`, `<=`, `>`, `<`, `AND`, `OR`, `NOT` and only match patterns after it. ## Commands