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
This commit is contained in:
@@ -1,34 +1,19 @@
|
|||||||
# AGENTS.md - tia-lsp
|
# AGENTS.md - tia-lsp.nvim
|
||||||
|
|
||||||
A Neovim/LazyVim plugin and standalone LSP server for Siemens TIA Portal languages. Currently supports **SCL**; planned support for STL/AWL, LAD, FBD. The project is split into two repos:
|
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`.
|
||||||
|
|
||||||
- **`tia-lsp`** (this repo) — LSP server (`src/`) + tree-sitter grammar (`grammar.js`). Editor-agnostic.
|
|
||||||
- **`tia-lsp.nvim`** — Neovim plugin (`lua/`) that launches the server and adds editor features.
|
|
||||||
|
|
||||||
## Build/Lint/Test Commands
|
## Build/Lint/Test Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# LSP Server
|
|
||||||
make start # Start LSP server
|
|
||||||
make test # Run LSP in test mode
|
|
||||||
lua src/main.lua --test # Direct test execution
|
|
||||||
|
|
||||||
# Tree-sitter Parser
|
|
||||||
tree-sitter generate # Generate parser from grammar.js
|
|
||||||
tree-sitter test # Run all parser tests
|
|
||||||
tree-sitter parse <file.scl> # Parse single SCL file
|
|
||||||
|
|
||||||
# Lua Linting
|
# Lua Linting
|
||||||
luacheck src/ # Lint LSP server code
|
luacheck lua/tia/ # Lint language modules
|
||||||
luacheck lua/tia/ # Lint plugin modules
|
luacheck lua/tia_lsp/ # Lint plugin entry point
|
||||||
|
|
||||||
# Unit & Integration Tests
|
# Unit & Integration Tests
|
||||||
lua test/run_tests.lua # Run all tests
|
lua test/run_tests.lua # Run all plugin tests
|
||||||
lua test/test_udt.lua # Run UDT parser tests
|
lua test/test_udt.lua # Run UDT parser tests
|
||||||
lua test/test_db.lua # Run DB parser tests
|
lua test/test_db.lua # Run DB parser tests
|
||||||
lua test/test_fb.lua # Run FB parser tests
|
lua test/test_fb.lua # Run FB parser tests
|
||||||
lua test/test_formatter.lua # Run formatter tests
|
|
||||||
lua test/test_plc_json.lua # Run plc_json tests
|
|
||||||
lua test/test_integration.lua # Run integration tests
|
lua test/test_integration.lua # Run integration tests
|
||||||
|
|
||||||
# Reference Project
|
# Reference Project
|
||||||
@@ -39,17 +24,8 @@ lua test/test_integration.lua # Run integration tests
|
|||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
tia-lsp/
|
tia-lsp.nvim/
|
||||||
├── src/ # LSP server (uses dofile)
|
├── lua/tia/ # Language modules (uses require)
|
||||||
│ ├── main.lua # Entry point, JSON-RPC protocol
|
|
||||||
│ ├── parser.lua # SCL parser (will become parser_scl.lua + dispatch)
|
|
||||||
│ ├── diagnostics.lua # Linter (source = "tia_lsp")
|
|
||||||
│ ├── formatter.lua # Document formatter
|
|
||||||
│ ├── treesitter.lua # Tree-sitter integration
|
|
||||||
│ ├── plc_json.lua # External UDT/DB loading (language-agnostic)
|
|
||||||
│ ├── builtin_instructions.lua # TIA Portal built-in types
|
|
||||||
│ └── json.lua # JSON encoder/decoder
|
|
||||||
├── lua/tia/ # Neovim plugin (uses require)
|
|
||||||
│ ├── udt_parser.lua # UDT parser
|
│ ├── udt_parser.lua # UDT parser
|
||||||
│ ├── db_parser.lua # Global DB parser
|
│ ├── db_parser.lua # Global DB parser
|
||||||
│ ├── fb_parser.lua # Function Block parser
|
│ ├── fb_parser.lua # Function Block parser
|
||||||
@@ -61,21 +37,20 @@ tia-lsp/
|
|||||||
├── lua/tia_lsp/ # Plugin entry point
|
├── lua/tia_lsp/ # Plugin entry point
|
||||||
│ ├── init.lua # Main plugin setup (vim.lsp.start)
|
│ ├── init.lua # Main plugin setup (vim.lsp.start)
|
||||||
│ └── plugins/scl.lua # LazyVim plugin spec
|
│ └── plugins/scl.lua # LazyVim plugin spec
|
||||||
├── queries/ # Tree-sitter queries (per-language: queries/scl/)
|
├── queries/scl/ # Tree-sitter queries (per-language)
|
||||||
└── grammar.js # Tree-sitter grammar (language name: 'scl')
|
└── ftdetect/scl.vim # Filetype detection
|
||||||
```
|
```
|
||||||
|
|
||||||
## Naming Convention
|
## Naming Convention
|
||||||
|
|
||||||
- **Project name** (repo, Lua module, LSP server, mason package): `tia-lsp` / `tia_lsp` — umbrella for all TIA languages.
|
- **Project name** (repo, Lua module): `tia-lsp.nvim` / `tia_lsp` — umbrella for all TIA languages.
|
||||||
- **Language name** (filetype, tree-sitter parser, grammar): `scl` — stays as-is. Future languages (`stl`, `lad`, `fbd`) get their own names.
|
- **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`).
|
- **LSP client name**: `tia_lsp` (one client handles all TIA languages; dispatches by `filetype` / `languageId`).
|
||||||
- **Diagnostic source**: `tia_lsp`.
|
|
||||||
- **Commands**: `:SCLFormat`, `:SCLShowVariables`, etc. — per-filetype prefix. Future: `:STLFormat`, etc.
|
- **Commands**: `:SCLFormat`, `:SCLShowVariables`, etc. — per-filetype prefix. Future: `:STLFormat`, etc.
|
||||||
|
|
||||||
## Filetype Detection
|
## Filetype Detection
|
||||||
|
|
||||||
After the split, `ftdetect/scl.vim` will live in `tia-lsp.nvim`. Currently required in Neovim config:
|
`ftdetect/scl.vim` registers the following extensions as `scl` filetype:
|
||||||
```vim
|
```vim
|
||||||
au BufRead,BufNewFile *.scl set filetype=scl
|
au BufRead,BufNewFile *.scl set filetype=scl
|
||||||
au BufRead,BufNewFile *.udt set filetype=scl
|
au BufRead,BufNewFile *.udt set filetype=scl
|
||||||
@@ -108,12 +83,10 @@ return M
|
|||||||
### Imports
|
### Imports
|
||||||
- `lua/tia/` modules: `require("tia.module_name")`
|
- `lua/tia/` modules: `require("tia.module_name")`
|
||||||
- `lua/tia_lsp/` entry: `require("tia_lsp")`
|
- `lua/tia_lsp/` entry: `require("tia_lsp")`
|
||||||
- `src/` modules: `dofile(script_path .. "/module.lua")`
|
|
||||||
- External dependencies: wrap in `pcall()` for safety
|
- External dependencies: wrap in `pcall()` for safety
|
||||||
|
|
||||||
### Code Formatting
|
### Code Formatting
|
||||||
- `lua/tia/` and `lua/tia_lsp/`: 2 spaces indentation
|
- `lua/tia/` and `lua/tia_lsp/`: 2 spaces indentation
|
||||||
- `src/`: tab-based indentation
|
|
||||||
- Max line length: 120 characters
|
- Max line length: 120 characters
|
||||||
- No trailing whitespace
|
- No trailing whitespace
|
||||||
|
|
||||||
@@ -189,7 +162,5 @@ Use the reference project for testing:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Contains `.scl`, `.db`, `.udt`, and `.xml` files for comprehensive testing of:
|
Contains `.scl`, `.db`, `.udt`, and `.xml` files for comprehensive testing of:
|
||||||
- LSP features (hover, completion, go-to-definition)
|
- Plugin features (auto-prefix, workspace scanning, attribute toggle)
|
||||||
- Formatter (`:SCLFormat`)
|
|
||||||
- Attribute block toggle
|
|
||||||
- Workspace type scanning
|
- Workspace type scanning
|
||||||
|
|||||||
@@ -1,56 +1,9 @@
|
|||||||
# tia-lsp - Language Server for Siemens TIA Portal
|
# tia-lsp.nvim - Neovim Plugin for Siemens TIA Portal
|
||||||
|
|
||||||
A Neovim/LazyVim plugin and standalone LSP server providing **Language Server Protocol (LSP)**, **Linting**, **Formatting**, and **Syntax Highlighting** for Siemens TIA Portal languages. Currently supports **SCL** (Structured Control Language), with planned support for **STL/AWL**, **LAD**, and **FBD**.
|
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**.
|
||||||
|
|
||||||
The project is split into two repos:
|
|
||||||
|
|
||||||
- **`tia-lsp`** (this repo) — the standalone LSP server (`src/`) plus the tree-sitter grammar (`grammar.js`, `src/parser.c`). Speaks JSON-RPC over stdio. Editor-agnostic: usable from Neovim, VS Code, or any LSP client.
|
|
||||||
- **`tia-lsp.nvim`** — the Neovim plugin (`lua/`) that launches the server via `vim.lsp.start()` and adds editor-specific features (auto-prefix, blink.cmp source, workspace UDT scanning, attribute toggle, multiline params).
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### LSP (Language Server Protocol)
|
|
||||||
| Feature | Description |
|
|
||||||
|---------|-------------|
|
|
||||||
| **Hover** | Press `K` to see variable type and struct fields |
|
|
||||||
| **Completion** | `#` for variables, `.` for struct fields, `(` for functions |
|
|
||||||
| **Go to Definition** | `gD` jump to function, FB, DB, or UDT definition |
|
|
||||||
| **Document Symbols** | Shows functions, variables, types |
|
|
||||||
| **Workspace Symbols** | Search across all `.scl` files with fuzzy matching |
|
|
||||||
| **Semantic Tokens** | Keywords, variables highlighted |
|
|
||||||
| **Inlay Hints** | Shows variable types after declaration |
|
|
||||||
| **External UDT** | Load types from `.plc.json` or `data_types/` folder |
|
|
||||||
|
|
||||||
### Linter (Diagnostics)
|
|
||||||
| Feature | Description |
|
|
||||||
|---------|-------------|
|
|
||||||
| **Undefined Variables** | Detects variables used without declaration |
|
|
||||||
| **Invalid # Prefix** | Detects `#` prefix in VAR sections |
|
|
||||||
| **Unknown Types** | Warns about unknown data types |
|
|
||||||
| **Type Validation** | Basic type checking |
|
|
||||||
|
|
||||||
### Formatter
|
|
||||||
| Feature | Description |
|
|
||||||
|---------|-------------|
|
|
||||||
| **Document Formatting** | Format entire SCL files with `:SCLFormat` |
|
|
||||||
| **Multi-line Assignments** | Preserves and formats multi-line expressions with proper alignment |
|
|
||||||
| **FB Call Formatting** | Formats function block calls in multiline style |
|
|
||||||
| **Indentation** | Proper block indentation |
|
|
||||||
| **Keyword Casing** | Maintains SCL keyword casing |
|
|
||||||
|
|
||||||
### Syntax Highlighting
|
|
||||||
- Full SCL syntax support via tree-sitter
|
|
||||||
- Block definitions: ORGANIZATION_BLOCK, FUNCTION_BLOCK, FUNCTION
|
|
||||||
- Variable sections: VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT, VAR_TEMP, VAR_CONSTANT
|
|
||||||
- Control structures: IF/THEN/ELSIF/ELSE/END_IF, CASE/OF/END_CASE, FOR/TO/BY/DO/END_FOR, WHILE/END_WHILE, REPEAT/UNTIL/END_REPEAT
|
|
||||||
- Built-in data types: BOOL, INT, DINT, REAL, STRING, TIME, etc.
|
|
||||||
- Function block instances highlighted in calls
|
|
||||||
- FB parameter names (IN, PT, CU, etc.) highlighted as properties
|
|
||||||
- `#` prefix for local variables highlighted
|
|
||||||
- Operators: AND, OR, NOT, XOR, MOD
|
|
||||||
- Comments: line (`//`), block (`(* *)`), C-style (`/* */`)
|
|
||||||
|
|
||||||
### Additional Features
|
|
||||||
| Feature | Description |
|
| Feature | Description |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| **Auto-Prefix** | Automatically adds `#` prefix to local variables (like TIA Portal) |
|
| **Auto-Prefix** | Automatically adds `#` prefix to local variables (like TIA Portal) |
|
||||||
@@ -60,65 +13,24 @@ The project is split into two repos:
|
|||||||
| **Workspace Global DB Scanner** | Scans project for global data blocks (.db files) |
|
| **Workspace Global DB Scanner** | Scans project for global data blocks (.db files) |
|
||||||
| **blink.cmp Integration** | Smart completion source for blink.cmp |
|
| **blink.cmp Integration** | Smart completion source for blink.cmp |
|
||||||
| **Attribute Block Toggle** | Interactive expand/collapse of `{...}` blocks with `<Leader>xa` |
|
| **Attribute Block Toggle** | Interactive expand/collapse of `{...}` blocks with `<Leader>xa` |
|
||||||
|
| **Tree-sitter Highlighting** | Syntax highlighting via tree-sitter queries |
|
||||||
|
|
||||||
## Installation
|
## 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. See [Mason installation](#mason-installation) below for the recommended path.
|
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
|
### LazyVim + Mason (Recommended)
|
||||||
```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 options
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Setup
|
1. Add the mason registry as a lazy.nvim plugin:
|
||||||
```lua
|
|
||||||
-- In your init.lua or plugin file
|
|
||||||
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://your-gitea/tia-lsp.git", -- tree-sitter parser source
|
|
||||||
cmp = true,
|
|
||||||
workspace_types = true,
|
|
||||||
auto_prefix = true,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mason installation
|
|
||||||
|
|
||||||
`tia-lsp` can be installed via [mason.nvim](https://github.com/mason-org/mason.nvim) using a custom registry. The setup below uses a `file:` registry sourced from a Gitea-hosted `tia-mason-registry` repo (cloned by lazy.nvim), and falls back to the official mason registry for any other packages.
|
|
||||||
|
|
||||||
1. Clone the registry repo as a lazy.nvim plugin:
|
|
||||||
```lua
|
```lua
|
||||||
-- ~/.config/nvim/lua/plugins/tia-mason-registry.lua
|
-- ~/.config/nvim/lua/plugins/tia-mason-registry.lua
|
||||||
return {
|
return {
|
||||||
url = "https://your-gitea/tia-mason-registry.git",
|
url = "https://gitea.l-tech.rs/lazar/tia-mason-registry.git",
|
||||||
name = "tia-mason-registry",
|
name = "tia-mason-registry",
|
||||||
lazy = false, -- must be available before mason setup
|
lazy = false, -- must be available before mason setup
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Register the registry with mason:
|
2. Register the registry with mason:
|
||||||
```lua
|
```lua
|
||||||
-- ~/.config/nvim/lua/plugins/mason.lua
|
-- ~/.config/nvim/lua/plugins/mason.lua
|
||||||
@@ -132,17 +44,54 @@ require("tia_lsp").setup({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
3. Install `yq` (one-time prerequisite for mason's `file:` registry):
|
|
||||||
```
|
|
||||||
:MasonInstall yq
|
|
||||||
```
|
|
||||||
4. Install the server:
|
|
||||||
```
|
|
||||||
:MasonInstall tia-lsp
|
|
||||||
```
|
|
||||||
5. Verify: open a `.scl` file and run `:LspInfo` — `tia_lsp` should be attached, and `vim.fn.exepath("tia-lsp")` should resolve to `~/.local/share/nvim/mason/bin/tia-lsp`.
|
|
||||||
|
|
||||||
To upgrade: tag a new `vX.Y.Z` release on the `tia-lsp` Gitea repo, bump `source.id` in `tia-mason-registry/packages/tia-lsp/package.yaml`, push the registry repo, then `:MasonInstall tia-lsp` again.
|
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
|
## Configuration Options
|
||||||
|
|
||||||
@@ -207,152 +156,65 @@ statCounter{...} : Int;
|
|||||||
- Use `<Leader>xae` to expand all blocks in buffer
|
- Use `<Leader>xae` to expand all blocks in buffer
|
||||||
- Use `<Leader>xac` to collapse all blocks
|
- Use `<Leader>xac` to collapse all blocks
|
||||||
|
|
||||||
**Note:** Collapsed blocks are automatically expanded before saving to preserve full content on disk. Use `:SCLFormat` to format, then `:SCLCollapseAllAttrBlocks` to collapse while preserving the ability to expand later.
|
**Note:** Collapsed blocks are automatically expanded before saving to preserve full content on disk.
|
||||||
|
|
||||||
## External UDT Support
|
## Filetype Detection
|
||||||
|
|
||||||
### Option 1: Create `.plc.json`
|
The plugin includes `ftdetect/scl.vim` which registers the following file extensions as `scl` filetype:
|
||||||
```json
|
- `*.scl` — SCL source files
|
||||||
{
|
- `*.udt` — User-defined type files
|
||||||
"dataTypes": [
|
- `*.db` — Data block files
|
||||||
{
|
|
||||||
"name": "equipmentData",
|
|
||||||
"struct": [
|
|
||||||
{"name": "id", "dataTypeName": "INT"},
|
|
||||||
{"name": "status", "dataTypeName": "BOOL"},
|
|
||||||
{"name": "temperature", "dataTypeName": "REAL"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 2: Auto-scan `data_types/` folder
|
## Project Structure
|
||||||
The LSP automatically scans for SCL files in `data_types/` folder and extracts TYPE definitions.
|
|
||||||
|
|
||||||
### Option 3: Generate `.plc.json`
|
|
||||||
```bash
|
|
||||||
lua ~/dev/tia-lsp/src/plc_json.lua --generate
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Data Block Support
|
|
||||||
|
|
||||||
The plugin automatically scans for `.db` files in the workspace and provides auto-completion for global data blocks and their members.
|
|
||||||
|
|
||||||
### How it works
|
|
||||||
|
|
||||||
1. **Workspace Scanning**: When opening a `.scl` file, the plugin scans the project for `.db` files
|
|
||||||
2. **DB Parsing**: Parses `DATA_BLOCK` definitions and extracts members from `VAR` sections
|
|
||||||
3. **Auto-completion**: Provides smart completion for DB names and members
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
- Type a space or `#` to trigger completion - global DB names appear alongside local variables
|
|
||||||
- Type `"` followed by a DB name to get member completions
|
|
||||||
- Access nested members: `"MyDB".member.submember`
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```scl
|
|
||||||
// Access global DB members
|
|
||||||
"HmiData".units
|
|
||||||
"Parameter".machine.unit100.paramTimeStampApplied
|
|
||||||
|
|
||||||
// Inside FB call parameter
|
|
||||||
#instParameterManager(hmiInterface:="HmiData".units, ...)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Project Structure
|
|
||||||
|
|
||||||
```
|
```
|
||||||
tia-lsp/
|
tia-lsp.nvim/
|
||||||
├── README.md # This file
|
├── lua/
|
||||||
├── package.json # NPM scripts (tree-sitter)
|
│ ├── tia_lsp/ # Plugin entry point
|
||||||
├── Makefile # Build commands
|
│ │ ├── init.lua # Main setup (vim.lsp.start, exepath detection)
|
||||||
├── grammar.js # Tree-sitter grammar (SCL language)
|
│ │ └── plugins/
|
||||||
├── tree-sitter.json # Parser config
|
│ │ └── scl.lua # LazyVim plugin spec (SCL filetype)
|
||||||
├── queries/ # Tree-sitter queries
|
│ └── tia/ # Language modules (shared across TIA languages)
|
||||||
│ └── scl/ # Per-language query dir (SCL)
|
│ ├── 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
|
│ ├── highlights.scm # Syntax highlighting
|
||||||
│ ├── indents.scm # Indentation
|
│ ├── indents.scm # Indentation
|
||||||
│ ├── folds.scm # Code folding
|
│ ├── folds.scm # Code folding
|
||||||
│ ├── locals.scm # Variable scoping
|
│ ├── locals.scm # Variable scoping
|
||||||
│ └── tags.scm # Navigation tags
|
│ └── tags.scm # Navigation tags
|
||||||
├── src/ # LSP server
|
├── ftdetect/
|
||||||
│ ├── main.lua # Main entry point (JSON-RPC over stdio)
|
│ └── scl.vim # Filetype detection
|
||||||
│ ├── parser.lua # SCL parser (will become parser_scl.lua + dispatch)
|
└── test/ # Plugin tests
|
||||||
│ ├── treesitter.lua # Tree-sitter integration
|
├── run_tests.lua
|
||||||
│ ├── json.lua # JSON encoder/decoder
|
├── test_harness.lua
|
||||||
│ ├── plc_json.lua # External UDT loading (language-agnostic)
|
├── test_udt.lua
|
||||||
│ ├── diagnostics.lua # Linter diagnostics
|
├── test_db.lua
|
||||||
│ └── formatter.lua # Document formatter
|
├── test_fb.lua
|
||||||
└── lua/
|
└── test_integration.lua
|
||||||
├── tia_lsp/ # Neovim plugin
|
|
||||||
│ ├── init.lua # Main plugin setup
|
|
||||||
│ └── plugins/
|
|
||||||
│ └── scl.lua # LazyVim plugin spec (SCL filetype)
|
|
||||||
└── tia/ # Language modules (shared across TIA languages)
|
|
||||||
├── auto_prefix.lua
|
|
||||||
├── attr_toggle.lua
|
|
||||||
├── blink_cmp_source.lua
|
|
||||||
├── builtin_instructions.lua
|
|
||||||
├── db_parser.lua
|
|
||||||
├── fb_parser.lua
|
|
||||||
├── multiline_params.lua
|
|
||||||
├── udt_parser.lua
|
|
||||||
├── variables.lua
|
|
||||||
└── workspace_types.lua
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building Tree-sitter Parser
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Generate parser from grammar.js
|
|
||||||
npm run build
|
|
||||||
# or: tree-sitter generate
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
npm test
|
|
||||||
# or: tree-sitter test
|
|
||||||
```
|
|
||||||
|
|
||||||
## SCL Code Example
|
|
||||||
|
|
||||||
```pascal
|
|
||||||
FUNCTION_BLOCK "Em101Sequence"
|
|
||||||
VAR
|
|
||||||
x : INT;
|
|
||||||
myVar : equipmentData; // User-defined type
|
|
||||||
END_VAR
|
|
||||||
|
|
||||||
IF x > 5 THEN
|
|
||||||
myVar.id := 10;
|
|
||||||
END_IF
|
|
||||||
|
|
||||||
myVar.status := TRUE;
|
|
||||||
myVar.temperature := 25.5;
|
|
||||||
|
|
||||||
END_FUNCTION_BLOCK
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The plugin uses a standalone LSP server (`src/main.lua`) that communicates via JSON-RPC over stdio. The server handles:
|
|
||||||
- Text document synchronization
|
|
||||||
- Diagnostics (linting)
|
|
||||||
- Formatting
|
|
||||||
- Completion, hover, go-to-definition
|
|
||||||
- Semantic tokens
|
|
||||||
|
|
||||||
The Neovim plugin (`lua/tia_lsp/init.lua`) manages:
|
|
||||||
- LSP client lifecycle via `vim.lsp.start()`
|
|
||||||
- FileType autocmd for lazy loading
|
|
||||||
- blink.cmp integration
|
|
||||||
- Workspace scanning for UDTs/DBs
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- **Neovim 0.11+** (uses `vim.lsp.start()`)
|
- **Neovim 0.11+** (uses `vim.lsp.start()`)
|
||||||
- **nvim-treesitter** - Syntax highlighting
|
- **tia-lsp** — the LSP server (install via mason or manually)
|
||||||
- **blink.cmp** - Optional, for completion
|
- **nvim-treesitter** — Syntax highlighting
|
||||||
- **Lua 5.1+** - LSP server runtime
|
- **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`).
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
au BufRead,BufNewFile *.scl set filetype=scl
|
||||||
|
au BufRead,BufNewFile *.udt set filetype=scl
|
||||||
|
au BufRead,BufNewFile *.db set filetype=scl
|
||||||
+2
-4
@@ -1,11 +1,11 @@
|
|||||||
#!/usr/bin/env lua
|
#!/usr/bin/env lua
|
||||||
|
|
||||||
package.path = package.path .. ";lua/?.lua;src/?.lua"
|
package.path = package.path .. ";lua/?.lua"
|
||||||
|
|
||||||
local test = dofile("test/test_harness.lua")
|
local test = dofile("test/test_harness.lua")
|
||||||
|
|
||||||
print("========================================")
|
print("========================================")
|
||||||
print(" SCL LSP - Test Suite")
|
print(" tia-lsp.nvim - Plugin Test Suite")
|
||||||
print("========================================")
|
print("========================================")
|
||||||
print(string.format("Reference Project: %s", test.REF_PROJECT))
|
print(string.format("Reference Project: %s", test.REF_PROJECT))
|
||||||
|
|
||||||
@@ -28,8 +28,6 @@ local function run_all_tests()
|
|||||||
run_test_file("test_udt.lua")
|
run_test_file("test_udt.lua")
|
||||||
run_test_file("test_db.lua")
|
run_test_file("test_db.lua")
|
||||||
run_test_file("test_fb.lua")
|
run_test_file("test_fb.lua")
|
||||||
run_test_file("test_formatter.lua")
|
|
||||||
run_test_file("test_plc_json.lua")
|
|
||||||
run_test_file("test_integration.lua")
|
run_test_file("test_integration.lua")
|
||||||
|
|
||||||
test.report()
|
test.report()
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
local test = dofile("test/test_harness.lua")
|
local test = dofile("test/test_harness.lua")
|
||||||
|
|
||||||
package.path = package.path .. ";lua/?.lua;src/?.lua"
|
package.path = package.path .. ";lua/?.lua"
|
||||||
|
|
||||||
local udt_parser = require("tia.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
local db_parser = require("tia.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
local fb_parser = require("tia.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
|
|
||||||
local script_path = debug.getinfo(1, "S").source:gsub("^@", ""):match("(.*/)") or "."
|
|
||||||
script_path = script_path:gsub("^test/", ""):gsub("/tests$", "")
|
|
||||||
if script_path == "" then script_path = "." end
|
|
||||||
|
|
||||||
local plc_json = dofile(script_path .. "/src/plc_json.lua")
|
|
||||||
|
|
||||||
local function scan_directory(dir, ext)
|
local function scan_directory(dir, ext)
|
||||||
local files = {}
|
local files = {}
|
||||||
local handle = io.popen('find "' .. dir .. '" -type f -name "*.' .. ext .. '" 2>/dev/null')
|
local handle = io.popen('find "' .. dir .. '" -type f -name "*.' .. ext .. '" 2>/dev/null')
|
||||||
@@ -234,9 +228,6 @@ local function run_integration_tests()
|
|||||||
|
|
||||||
print("\n--- Type Resolution ---")
|
print("\n--- Type Resolution ---")
|
||||||
test_type_resolution()
|
test_type_resolution()
|
||||||
|
|
||||||
print("\n--- Workspace Type Loading ---")
|
|
||||||
test_workspace_type_loading()
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user