# tia-lsp.nvim - Neovim Plugin for Siemens TIA Portal A Neovim/LazyVim plugin that launches the [`tia-lsp`](https://gitea.l-tech.rs/lazar/tia-lsp) language server and adds editor-specific features for Siemens TIA Portal languages. Currently supports **SCL** (Structured Control Language), with planned support for **STL/AWL**, **LAD**, and **FBD**. ## Features | Feature | Description | |---------|-------------| | **Auto-Prefix** | Automatically adds `#` prefix to local variables (like TIA Portal) | | **Multiline Parameters** | Fill FB/Function call parameters across multiple lines with `mp` or `:SCLMultilineParams` | | **Tab Navigation** | Jump between parameters with `` in insert mode | | **Workspace UDT Scanner** | Scans project for user-defined types (.udt files) | | **Workspace Global DB Scanner** | Scans project for global data blocks (.db files) | | **blink.cmp Integration** | Smart completion source for blink.cmp | | **Attribute Block Toggle** | Interactive expand/collapse of `{...}` blocks with `xa` | | **Tree-sitter Highlighting** | Syntax highlighting via tree-sitter queries | ## Installation The plugin auto-detects the mason-installed `tia-lsp` executable via `vim.fn.exepath("tia-lsp")` and falls back to `lua ` for manual installs. ### LazyVim + Mason (Recommended) 1. Add the mason registry as a lazy.nvim plugin: ```lua -- ~/.config/nvim/lua/plugins/tia-mason-registry.lua return { url = "https://gitea.l-tech.rs/lazar/tia-mason-registry.git", name = "tia-mason-registry", lazy = false, -- must be available before mason setup } ``` 2. Register the registry with mason: ```lua -- ~/.config/nvim/lua/plugins/mason.lua return { "mason-org/mason.nvim", opts = { registries = { "file:" .. vim.fn.expand("~/.local/share/nvim/lazy/tia-mason-registry"), "github:mason-org/mason-registry", }, }, } ``` 3. Add the plugin: ```lua -- ~/.config/nvim/lua/plugins/tia-lsp.lua return { "lazar/tia-lsp.nvim", ft = "scl", lazy = true, dependencies = { "neovim/nvim-lspconfig", "nvim-treesitter/nvim-treesitter", "saghen/blink.cmp", }, config = function() require("tia_lsp").setup({ lsp = { on_attach = function(client, bufnr) -- custom keymaps end, }, cmp = true, -- Enable blink.cmp integration workspace_types = true, -- Enable workspace UDT scanning auto_prefix = true, -- Enable auto-# prefixing debug = false, }) end, } ``` 4. Install: ``` :MasonInstall yq " one-time prerequisite for file: registry :MasonInstall tia-lsp " installs the LSP server ``` 5. Verify: open a `.scl` file and run `:LspInfo` — `tia_lsp` should be attached. ### Manual Setup (without Mason) ```lua require("tia_lsp").setup({ server_path = "/path/to/tia-lsp/src/main.lua", -- only used if tia-lsp is not on PATH ts_parser_url = "https://gitea.l-tech.rs/lazar/tia-lsp.git", cmp = true, workspace_types = true, auto_prefix = true, }) ``` ## Configuration Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `server_path` | string | `~/dev/tia-lsp/src/main.lua` | Path to LSP server (only used if `tia-lsp` is not on PATH) | | `ts_parser_url` | string | `file://~/dev/tia-lsp` | URL passed to nvim-treesitter for the SCL parser source | | `lsp.on_attach` | function | `nil` | Custom LSP attach callback | | `lsp.formatting` | boolean | `false` | Enable format command | | `cmp` | boolean | `true` | Enable blink.cmp integration | | `workspace_types` | boolean | `true` | Enable workspace UDT scanning | | `auto_prefix` | boolean | `true` | Enable auto-# prefixing | | `project_root` | string | `nil` | Custom project root | | `library_paths` | table | `{}` | Additional library paths | | `debug` | boolean | `false` | Enable debug logging | ## Commands | Command | Description | |---------|-------------| | `:SCLShowVariables` | Show local variables in current file | | `:SCLShowWorkspaceTypes` | Show workspace UDTs, FBs, and Global DBs count | | `:SCLRescanWorkspaceTypes` | Rescan workspace for types and DBs | | `:SCLPrefixWord` | Manually prefix current word with `#` | | `:SCLMultilineParams` | Fill multiline parameters for FB/Function call | | `:SCLFormat` | Format current SCL file | | `:SCLToggleAttrBlock` | Toggle attribute block under cursor | | `:SCLExpandAllAttrBlocks` | Expand all `{...}` blocks in buffer | | `:SCLCollapseAllAttrBlocks` | Collapse all matching attribute blocks | ## Keybindings | Key | Action | |-----|--------| | `K` | Hover (show variable info) | | `gd` | Go to Definition | | `s` | Document Symbols | | `w` | Workspace Symbols | | `mp` | Fill multiline FB/Function parameters (insert mode) | | `` | Jump to next parameter (insert mode) | | `xa` | Toggle attribute block under cursor | | `xae` | Expand all attribute blocks | | `xac` | Collapse all attribute blocks | | `fw` | Workspace Symbols (Telescope) | | `ca` | Code Actions | ## Attribute Block Toggle Interactive expand/collapse of SCL variable attribute blocks: ```scl -- Before: Verbose attributes statCounter{EXTERNALACCESSIBLE := 'false'; EXTERNALVISIBLE := 'false'} : Int; -- After: Collapsed statCounter{...} : Int; ``` **Usage:** - Place cursor inside any `{...}` block - Press `xa` to toggle expand/collapse - Use `xae` to expand all blocks in buffer - Use `xac` to collapse all blocks **Note:** Collapsed blocks are automatically expanded before saving to preserve full content on disk. ## Filetype Detection The plugin includes `ftdetect/scl.vim` which registers the following file extensions as `scl` filetype: - `*.scl` — SCL source files - `*.udt` — User-defined type files - `*.db` — Data block files ## Project Structure ``` tia-lsp.nvim/ ├── lua/ │ ├── tia_lsp/ # Plugin entry point │ │ ├── init.lua # Main setup (vim.lsp.start, exepath detection) │ │ └── plugins/ │ │ └── scl.lua # LazyVim plugin spec (SCL filetype) │ └── tia/ # Language modules (shared across TIA languages) │ ├── auto_prefix.lua # Auto-# prefixing │ ├── attr_toggle.lua # Attribute block expand/collapse │ ├── blink_cmp_source.lua # blink.cmp completion source │ ├── builtin_instructions.lua │ ├── db_parser.lua # Global DB parser │ ├── fb_parser.lua # Function Block parser │ ├── multiline_params.lua # Multiline parameter filling │ ├── udt_parser.lua # UDT parser │ ├── variables.lua # Variable extraction │ └── workspace_types.lua # Workspace scanning ├── queries/ │ └── scl/ # Tree-sitter queries (per-language) │ ├── highlights.scm # Syntax highlighting │ ├── indents.scm # Indentation │ ├── folds.scm # Code folding │ ├── locals.scm # Variable scoping │ └── tags.scm # Navigation tags ├── ftdetect/ │ └── scl.vim # Filetype detection └── test/ # Plugin tests ├── run_tests.lua ├── test_harness.lua ├── test_udt.lua ├── test_db.lua ├── test_fb.lua └── test_integration.lua ``` ## Requirements - **Neovim 0.11+** (uses `vim.lsp.start()`) - **tia-lsp** — the LSP server (install via mason or manually) - **nvim-treesitter** — Syntax highlighting - **blink.cmp** — Optional, for completion - **Lua 5.1+** — LSP server runtime ## Testing ```bash lua test/run_tests.lua ``` Tests use files from a reference project (override with `SCL_REF_PROJECT=/path/to/ref lua test/run_tests.lua`).