Rename the project from scl_lsp to tia_lsp to future-proof for additional
TIA Portal languages (STL/AWL, LAD, FBD). The 'scl' language name is kept
as-is for the SCL filetype and tree-sitter parser; future languages get
their own names (stl, lad, fbd).
Project-wide changes:
- lua/scl_lsp/ → lua/tia_lsp/ (plugin entry point)
- lua/scl/ → lua/tia/ (language modules, shared across TIA langs)
- require('scl.*') → require('tia.*')
- require('scl_lsp') → require('tia_lsp')
- LSP client name: 'scl_lsp' → 'tia_lsp'
- Diagnostic source: 'scl_lsp' → 'tia_lsp' (src/diagnostics.lua)
- package.json name: 'scl-language-server' → 'tia-lsp'
lua/tia_lsp/init.lua:
- Add vim.fn.exepath('tia-lsp') detection so the plugin prefers the
mason-installed executable and falls back to { 'lua', server_path }
for manual installs.
- Add opts.ts_parser_url to configure the tree-sitter parser source.
- Remove hardcoded ~/dev/scl_lsp paths in favor of ~/dev/tia-lsp and
the new ts_parser_url option.
Cleanup:
- Delete lua/tia/init.lua (dead code, unreferenced duplicate of tia_lsp).
- Delete scl_lsp.sh (replaced by the mason-installed bin/tia-lsp wrapper).
- Update README.md and AGENTS.md to reflect the two-repo split
(tia-lsp server + tia-lsp.nvim plugin) and document Mason installation.
Unchanged (intentional):
- grammar.js, src/grammar.json, src/parser.c: tree-sitter language name
remains 'scl' (it's the language, not the project).
- Filetype patterns ('scl', 'udt', 'db') in autocmds.
- :SCL* command names (per-filetype prefix; future :STL* etc.).
Tests pass with the same pre-existing failures as before the rename
(formatter VAR_INPUT test, udt_parser line 199 const-variable bug,
plc_json reference-project-missing). No new failures introduced.
196 lines
6.5 KiB
Markdown
196 lines
6.5 KiB
Markdown
# AGENTS.md - tia-lsp
|
|
|
|
A Neovim/LazyVim plugin and standalone LSP server for Siemens TIA Portal languages. Currently supports **SCL**; planned support for STL/AWL, LAD, FBD. The project is split into two repos:
|
|
|
|
- **`tia-lsp`** (this repo) — LSP server (`src/`) + tree-sitter grammar (`grammar.js`). Editor-agnostic.
|
|
- **`tia-lsp.nvim`** — Neovim plugin (`lua/`) that launches the server and adds editor features.
|
|
|
|
## Build/Lint/Test Commands
|
|
|
|
```bash
|
|
# LSP Server
|
|
make start # Start LSP server
|
|
make test # Run LSP in test mode
|
|
lua src/main.lua --test # Direct test execution
|
|
|
|
# Tree-sitter Parser
|
|
tree-sitter generate # Generate parser from grammar.js
|
|
tree-sitter test # Run all parser tests
|
|
tree-sitter parse <file.scl> # Parse single SCL file
|
|
|
|
# Lua Linting
|
|
luacheck src/ # Lint LSP server code
|
|
luacheck lua/tia/ # Lint plugin modules
|
|
|
|
# Unit & Integration Tests
|
|
lua test/run_tests.lua # Run all tests
|
|
lua test/test_udt.lua # Run UDT parser tests
|
|
lua test/test_db.lua # Run DB parser tests
|
|
lua test/test_fb.lua # Run FB parser tests
|
|
lua test/test_formatter.lua # Run formatter tests
|
|
lua test/test_plc_json.lua # Run plc_json tests
|
|
lua test/test_integration.lua # Run integration tests
|
|
|
|
# Reference Project
|
|
# Tests use files from: ~/dev/siemens/projects/scl_lang_support_lazyvim_ref_project/
|
|
# Override with: SCL_REF_PROJECT=/path/to/ref lua tests/run_tests.lua
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
tia-lsp/
|
|
├── src/ # LSP server (uses dofile)
|
|
│ ├── main.lua # Entry point, JSON-RPC protocol
|
|
│ ├── parser.lua # SCL parser (will become parser_scl.lua + dispatch)
|
|
│ ├── diagnostics.lua # Linter (source = "tia_lsp")
|
|
│ ├── formatter.lua # Document formatter
|
|
│ ├── treesitter.lua # Tree-sitter integration
|
|
│ ├── plc_json.lua # External UDT/DB loading (language-agnostic)
|
|
│ ├── builtin_instructions.lua # TIA Portal built-in types
|
|
│ └── json.lua # JSON encoder/decoder
|
|
├── lua/tia/ # Neovim plugin (uses require)
|
|
│ ├── udt_parser.lua # UDT parser
|
|
│ ├── db_parser.lua # Global DB parser
|
|
│ ├── fb_parser.lua # Function Block parser
|
|
│ ├── variables.lua # Variable extraction
|
|
│ ├── workspace_types.lua # Workspace scanning
|
|
│ ├── multiline_params.lua
|
|
│ ├── auto_prefix.lua
|
|
│ └── attr_toggle.lua
|
|
├── lua/tia_lsp/ # Plugin entry point
|
|
│ ├── init.lua # Main plugin setup (vim.lsp.start)
|
|
│ └── plugins/scl.lua # LazyVim plugin spec
|
|
├── queries/ # Tree-sitter queries (per-language: queries/scl/)
|
|
└── grammar.js # Tree-sitter grammar (language name: 'scl')
|
|
```
|
|
|
|
## Naming Convention
|
|
|
|
- **Project name** (repo, Lua module, LSP server, mason package): `tia-lsp` / `tia_lsp` — umbrella for all TIA languages.
|
|
- **Language name** (filetype, tree-sitter parser, grammar): `scl` — stays as-is. Future languages (`stl`, `lad`, `fbd`) get their own names.
|
|
- **LSP client name**: `tia_lsp` (one client handles all TIA languages; dispatches by `filetype` / `languageId`).
|
|
- **Diagnostic source**: `tia_lsp`.
|
|
- **Commands**: `:SCLFormat`, `:SCLShowVariables`, etc. — per-filetype prefix. Future: `:STLFormat`, etc.
|
|
|
|
## Filetype Detection
|
|
|
|
After the split, `ftdetect/scl.vim` will live in `tia-lsp.nvim`. Currently required in Neovim config:
|
|
```vim
|
|
au BufRead,BufNewFile *.scl set filetype=scl
|
|
au BufRead,BufNewFile *.udt set filetype=scl
|
|
au BufRead,BufNewFile *.db set filetype=scl
|
|
```
|
|
|
|
## Code Style Guidelines
|
|
|
|
### Module Pattern
|
|
```lua
|
|
local M = {}
|
|
|
|
local cache = {}
|
|
|
|
function M.public_function()
|
|
end
|
|
|
|
local function private_function()
|
|
end
|
|
|
|
return M
|
|
```
|
|
|
|
### Naming Conventions
|
|
- Functions/variables: `snake_case` (e.g., `get_udt_members`)
|
|
- Constants: `UPPER_SNAKE_CASE` (e.g., `PROJECT_MARKERS`)
|
|
- Private functions: declare as `local function` before public functions
|
|
- Booleans: prefix with `is_`, `has_` (e.g., `is_udt`, `has_members`)
|
|
|
|
### Imports
|
|
- `lua/tia/` modules: `require("tia.module_name")`
|
|
- `lua/tia_lsp/` entry: `require("tia_lsp")`
|
|
- `src/` modules: `dofile(script_path .. "/module.lua")`
|
|
- External dependencies: wrap in `pcall()` for safety
|
|
|
|
### Code Formatting
|
|
- `lua/tia/` and `lua/tia_lsp/`: 2 spaces indentation
|
|
- `src/`: tab-based indentation
|
|
- Max line length: 120 characters
|
|
- No trailing whitespace
|
|
|
|
### Comments
|
|
Add comments to explain non-obvious code sections, complex logic, and public API functions. Keep comments concise and relevant.
|
|
|
|
### Parser Module API
|
|
All parser modules must implement:
|
|
- `parse_*_file(filepath)` - Parse and cache
|
|
- `parse_*_content(content, filename)` - Parse string
|
|
- `get_*(name)` - Get cached item
|
|
- `get_all_*_names()` - List all cached names
|
|
- `get_*_members(name)` - Get members/fields
|
|
- `is_*_type(name)` - Check if known type
|
|
- `clear_cache()` - Clear cached data
|
|
- `get_cache_count()` - Return cache size
|
|
|
|
### Cache Management
|
|
```lua
|
|
local cache = {}
|
|
|
|
function M.clear_cache()
|
|
for k in pairs(cache) do
|
|
cache[k] = nil
|
|
end
|
|
end
|
|
```
|
|
|
|
### Error Handling
|
|
```lua
|
|
function M.parse_file(filepath)
|
|
local file = io.open(filepath, "r")
|
|
if not file then
|
|
return nil, "Cannot open file: " .. filepath
|
|
end
|
|
return result
|
|
end
|
|
|
|
-- External dependencies
|
|
local ok, module = pcall(require, "some_module")
|
|
if ok and module then
|
|
module.do_something()
|
|
end
|
|
```
|
|
|
|
## Lua Reserved Keywords
|
|
|
|
Cannot use reserved keywords as table keys directly:
|
|
```lua
|
|
-- WRONG
|
|
local range = { start = pos1, end = pos2 }
|
|
|
|
-- CORRECT
|
|
local range = { start = pos1, ["end"] = pos2 }
|
|
```
|
|
|
|
Critical for LSP range objects with `end` field.
|
|
|
|
## Type System
|
|
|
|
The LSP validates data types and provides warnings for unknown types:
|
|
- **Elementary**: BOOL, BYTE, WORD, DWORD, INT, DINT, REAL, TIME, STRING, etc.
|
|
- **Timer/counter**: IEC_TIMER, TON_TIME, TOF_TIME, CTU, CTD, CTUD
|
|
- **Built-in FBs**: TON, TOF, TP, R_TRIG, F_TRIG, etc.
|
|
- **Built-in functions**: ADD, SUB, MUL, DIV, SIN, COS, SQRT, etc.
|
|
- **Workspace types**: UDTs from `.udt` files, DBs from `.db` files
|
|
|
|
## Testing
|
|
|
|
Use the reference project for testing:
|
|
```
|
|
~/dev/siemens/projects/scl_lang_support_lazyvim_ref_project/
|
|
```
|
|
|
|
Contains `.scl`, `.db`, `.udt`, and `.xml` files for comprehensive testing of:
|
|
- LSP features (hover, completion, go-to-definition)
|
|
- Formatter (`:SCLFormat`)
|
|
- Attribute block toggle
|
|
- Workspace type scanning
|