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
8.0 KiB
tia-lsp.nvim - Neovim Plugin for Siemens TIA Portal
A Neovim/LazyVim plugin that launches the 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 <Space>mp or :SCLMultilineParams |
| Tab Navigation | Jump between parameters with <Tab> 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 <Leader>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 <server_path> for manual installs.
LazyVim + Mason (Recommended)
-
Add the mason registry as a lazy.nvim plugin:
-- ~/.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 } -
Register the registry with mason:
-- ~/.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", }, }, } -
Add the plugin:
-- ~/.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, } -
Install:
:MasonInstall yq " one-time prerequisite for file: registry :MasonInstall tia-lsp " installs the LSP server -
Verify: open a
.sclfile and run:LspInfo—tia_lspshould be attached.
Manual Setup (without Mason)
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 |
<leader>s |
Document Symbols |
<leader>w |
Workspace Symbols |
<Space>mp |
Fill multiline FB/Function parameters (insert mode) |
<Tab> |
Jump to next parameter (insert mode) |
<Leader>xa |
Toggle attribute block under cursor |
<Leader>xae |
Expand all attribute blocks |
<Leader>xac |
Collapse all attribute blocks |
<leader>fw |
Workspace Symbols (Telescope) |
<leader>ca |
Code Actions |
Attribute Block Toggle
Interactive expand/collapse of SCL variable attribute blocks:
-- Before: Verbose attributes
statCounter{EXTERNALACCESSIBLE := 'false'; EXTERNALVISIBLE := 'false'} : Int;
-- After: Collapsed
statCounter{...} : Int;
Usage:
- Place cursor inside any
{...}block - Press
<Leader>xato toggle expand/collapse - Use
<Leader>xaeto expand all blocks in buffer - Use
<Leader>xacto 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
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).