Files
tia-lsp.nvim/README.md
T

275 lines
11 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 |
## Architecture
This plugin is part of a three-repo ecosystem:
```
tia-lsp.nvim ← Neovim plugin (you are here) — launches the LSP client,
│ adds editor features (auto-# prefix, blink.cmp source,
│ attribute toggle, workspace scanning, ...)
tia-lsp ← Standalone LSP server — reads JSON-RPC over stdio,
│ provides hover, completion, diagnostics, formatting
│ Requires: lua src/main.lua + compiled parser.so
tia-mason-registry ← mason.nvim recipe — tells mason how to download tia-lsp,
compile parser.so, and create the tia-lsp executable
```
- **tia-lsp** ([repo](https://gitea.l-tech.rs/lazar/tia-lsp)) — the actual language server, editor-agnostic
- **tia-mason-registry** ([repo](https://gitea.l-tech.rs/lazar/tia-mason-registry)) — a single `package.yaml` for mason
- **tia-lsp.nvim** (this repo) — the Neovim-specific integration
## Installation
The plugin auto-detects the mason-installed `tia-lsp` executable via `vim.fn.exepath("tia-lsp")`. If not found, it falls back to running `lua <server_path>` (configurable).
### LazyVim + Mason (Recommended)
Mason downloads and builds the `tia-lsp` server; this plugin configures the LSP client.
**Step 1: Add the mason registry as a lazy.nvim plugin**
mason needs a local copy of the registry to read the install recipe. Adding it as a lazy.nvim plugin gets it cloned automatically:
```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",
}
```
**Step 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",
},
},
}
```
> **Why this works:** lazy.nvim clones `tia-mason-registry` to `~/.local/share/nvim/lazy/tia-mason-registry`. The `file:` registry path points mason to that directory, where it finds `packages/tia-lsp/package.yaml`. Mason needs [`yq`](https://github.com/mikefarah/yq) to parse YAML custom registries — install it once: `:MasonInstall yq`
**Step 3: Install the LSP server**
```
:MasonInstall tia-lsp
```
This clones `tia-lsp` from the source repo, compiles `parser.so` (tree-sitter), and places the `tia-lsp` executable on PATH.
**Step 4: Add this plugin**
```lua
-- ~/.config/nvim/lua/plugins/tia-lsp.lua
return {
"https://gitea.l-tech.rs/lazar/tia-lsp.nvim",
ft = "scl",
lazy = true,
dependencies = {
"neovim/nvim-lspconfig",
"nvim-treesitter/nvim-treesitter",
"saghen/blink.cmp", -- optional, for completion
},
opts = {
lsp = {
-- on_attach = function(client, bufnr) ... end,
},
cmp = true, -- enable blink.cmp integration
workspace_types = true, -- scan project for UDTs and DBs
auto_prefix = true, -- auto-# prefix for local variables
},
}
```
**Step 5: Verify**
Open a `.scl` file and run `:LspInfo`. `tia_lsp` should be attached. Run `:checkhealth` for detailed diagnostics.
---
### Manual Setup (without Mason)
Clone and build the server manually, then point the plugin at it.
```bash
# 1. Clone the LSP server
git clone --depth 1 https://gitea.l-tech.rs/lazar/tia-lsp.git ~/tia-lsp
# 2. Compile the tree-sitter parser (required for parsing SCL)
cc -fPIC -I ~/tia-lsp/src/tree_sitter -c ~/tia-lsp/src/parser.c -o ~/tia-lsp/parser.o
cc -shared ~/tia-lsp/parser.o -o ~/tia-lsp/parser.so
```
Then configure the plugin. The plugin first looks for `tia-lsp` on PATH; if not found, it falls back to running `lua <server_path>`. The tree-sitter parser is loaded from `parser.so` adjacent to the server.
```lua
require("tia_lsp").setup({
server_path = "~/tia-lsp/src/main.lua", -- fallback if tia-lsp 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 (fallback when `tia-lsp` not on PATH; change for manual installs) |
| `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
## Troubleshooting
| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| `:MasonInstall tia-lsp` fails with "unknown package" | Custom registry not registered with mason | Check the `registries` list in your mason config includes the `file:` path to the cloned registry |
| `:MasonInstall tia-lsp` fails with "yq: command not found" | Mason needs `yq` to parse the YAML registry | Run `:MasonInstall yq` to install it, then retry `:MasonInstall tia-lsp` |
| `tia_lsp` not attached after opening `.scl` | Server not on PATH or `server_path` points to wrong location | Run `:LspInfo` to see what server is configured. Check `vim.fn.exepath("tia-lsp")` returns a path |
| LSP starts but no syntax highlighting | Tree-sitter parser (`parser.so`) not found | mason install generates it automatically. For manual setups, run the `cc` commands in the Manual Setup section |
| `parser.so` load error at startup | Compiled for wrong architecture or Lua version | Recompile: clean the old `.so` and run the `cc` build commands again |
| LSP starts but no completions/hover | Server can't find its modules | Mason installs to a managed directory. If using manual setup, ensure `src/` contents are present next to `main.lua` |
## 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`).