# 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 ```bash # 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 by `filetype` / `languageId`). - **Commands**: `:SCLFormat`, `:SCLShowVariables`, etc. — per-filetype prefix. Future: `:STLFormat`, etc. ## Filetype Detection `ftdetect/scl.vim` registers the following extensions as `scl` filetype: ```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")` - External dependencies: wrap in `pcall()` for safety ### Code Formatting - `lua/tia/` and `lua/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 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: - Plugin features (auto-prefix, workspace scanning, attribute toggle) - Workspace type scanning