Files
lazar 8255db7f64 chore: adjust for plugin-only scope after split
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
2026-07-18 11:46:18 +02:00

221 lines
8.0 KiB
Markdown

# 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 `<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)
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 |
| `<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:
```scl
-- Before: Verbose attributes
statCounter{EXTERNALACCESSIBLE := 'false'; EXTERNALVISIBLE := 'false'} : Int;
-- After: Collapsed
statCounter{...} : Int;
```
**Usage:**
- Place cursor inside any `{...}` block
- Press `<Leader>xa` to toggle expand/collapse
- Use `<Leader>xae` to expand all blocks in buffer
- Use `<Leader>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`).