After splitting from the original scl_lsp repo, update for plugin-only scope: - README.md: focused on Neovim plugin installation, configuration, commands, keybindings, and editor features - AGENTS.md: plugin-only project structure and conventions - test/run_tests.lua: run only plugin tests (udt, db, fb, integration) - test/test_integration.lua: remove plc_json dependency (server module) - queries/: move from queries/*.scm to queries/scl/*.scm (nvim-treesitter per-language convention) - ftdetect/scl.vim: add filetype detection for .scl/.udt/.db files
5.0 KiB
5.0 KiB
AGENTS.md - tia-lsp.nvim
Neovim plugin for Siemens TIA Portal languages. Currently supports SCL; planned support for STL/AWL, LAD, FBD. This repo is the plugin only (lua/ + tree-sitter queries). The companion LSP server lives in tia-lsp.
Build/Lint/Test Commands
# Lua Linting
luacheck lua/tia/ # Lint language modules
luacheck lua/tia_lsp/ # Lint plugin entry point
# Unit & Integration Tests
lua test/run_tests.lua # Run all plugin 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_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.nvim/
├── lua/tia/ # Language modules (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/scl/ # Tree-sitter queries (per-language)
└── ftdetect/scl.vim # Filetype detection
Naming Convention
- Project name (repo, Lua module):
tia-lsp.nvim/tia_lsp— umbrella for all TIA languages. - Language name (filetype, tree-sitter parser):
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 byfiletype/languageId). - Commands:
:SCLFormat,:SCLShowVariables, etc. — per-filetype prefix. Future::STLFormat, etc.
Filetype Detection
ftdetect/scl.vim registers the following extensions as scl filetype:
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
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 functionbefore 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")- External dependencies: wrap in
pcall()for safety
Code Formatting
lua/tia/andlua/tia_lsp/: 2 spaces 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 cacheparse_*_content(content, filename)- Parse stringget_*(name)- Get cached itemget_all_*_names()- List all cached namesget_*_members(name)- Get members/fieldsis_*_type(name)- Check if known typeclear_cache()- Clear cached dataget_cache_count()- Return cache size
Cache Management
local cache = {}
function M.clear_cache()
for k in pairs(cache) do
cache[k] = nil
end
end
Error Handling
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:
-- 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
.udtfiles, DBs from.dbfiles
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:
- Plugin features (auto-prefix, workspace scanning, attribute toggle)
- Workspace type scanning