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
This commit is contained in:
@@ -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 <file.scl> # Parse a single SCL file
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
scl_lsp/
|
||||
├── src/ # LSP server implementation
|
||||
│ ├── main.lua # Entry point, protocol handling
|
||||
├── src/ # Standalone LSP server (uses dofile)
|
||||
│ ├── main.lua # Entry point, JSON-RPC protocol
|
||||
│ ├── 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
|
||||
│ └── 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user