diff --git a/AGENTS.md b/AGENTS.md index 6317527..81aa766 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,9 @@ -# AGENTS.md - SCL Language Server +# AGENTS.md - tia-lsp -A Neovim/LazyVim plugin for Siemens SCL language support providing LSP, linting, formatting, syntax highlighting, and auto-completion. +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: + +- **`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 @@ -17,7 +20,7 @@ tree-sitter parse # Parse single SCL file # Lua Linting luacheck src/ # Lint LSP server code -luacheck lua/scl/ # Lint plugin modules +luacheck lua/tia/ # Lint plugin modules # Unit & Integration Tests lua test/run_tests.lua # Run all tests @@ -36,18 +39,17 @@ lua test/test_integration.lua # Run integration tests ## Project Structure ``` -scl_lsp/ +tia-lsp/ ├── src/ # LSP server (uses dofile) │ ├── main.lua # Entry point, JSON-RPC protocol -│ ├── parser.lua # SCL parser -│ ├── diagnostics.lua # Linter +│ ├── 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 +│ ├── plc_json.lua # External UDT/DB loading (language-agnostic) │ ├── builtin_instructions.lua # TIA Portal built-in types │ └── json.lua # JSON encoder/decoder -├── lua/scl/ # Neovim plugin (uses require) -│ ├── init.lua # Main plugin setup +├── lua/tia/ # Neovim plugin (uses require) │ ├── udt_parser.lua # UDT parser │ ├── db_parser.lua # Global DB parser │ ├── fb_parser.lua # Function Block parser @@ -56,13 +58,24 @@ scl_lsp/ │ ├── multiline_params.lua │ ├── auto_prefix.lua │ └── attr_toggle.lua -├── queries/ # Tree-sitter queries -└── grammar.js # Tree-sitter grammar +├── lua/tia_lsp/ # Plugin entry point +│ ├── init.lua # Main plugin setup (vim.lsp.start) +│ └── plugins/scl.lua # LazyVim plugin spec +├── queries/ # Tree-sitter queries (per-language: queries/scl/) +└── grammar.js # Tree-sitter grammar (language name: 'scl') ``` +## Naming Convention + +- **Project name** (repo, Lua module, LSP server, mason package): `tia-lsp` / `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. +- **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. + ## Filetype Detection -Required in Neovim config (ftdetect/scl.vim): +After the split, `ftdetect/scl.vim` will live in `tia-lsp.nvim`. Currently required in Neovim config: ```vim au BufRead,BufNewFile *.scl set filetype=scl au BufRead,BufNewFile *.udt set filetype=scl @@ -93,12 +106,13 @@ return M - Booleans: prefix with `is_`, `has_` (e.g., `is_udt`, `has_members`) ### Imports -- `lua/scl/` modules: `require("scl.module_name")` +- `lua/tia/` modules: `require("tia.module_name")` +- `lua/tia_lsp/` entry: `require("tia_lsp")` - `src/` modules: `dofile(script_path .. "/module.lua")` - External dependencies: wrap in `pcall()` for safety ### Code Formatting -- `lua/scl/`: 2 spaces indentation +- `lua/tia/` and `lua/tia_lsp/`: 2 spaces indentation - `src/`: tab-based indentation - Max line length: 120 characters - No trailing whitespace diff --git a/README.md b/README.md index 6e177c1..b7541f5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ -# SCL Language Server - Unified Plugin for Siemens SCL +# tia-lsp - Language Server for Siemens TIA Portal -A comprehensive Neovim/LazyVim plugin providing **Language Server Protocol (LSP)**, **Linting**, **Formatting**, and **Syntax Highlighting** for Siemens SCL (Structured Control Language) used in 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**. + +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 @@ -58,11 +63,13 @@ A comprehensive Neovim/LazyVim plugin providing **Language Server Protocol (LSP) ## 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. See [Mason installation](#mason-installation) below for the recommended path. + ### LazyVim ```lua --- ~/.config/nvim/lua/plugins/scl.lua +-- ~/.config/nvim/lua/plugins/tia-lsp.lua return { - "lazar/scl_lsp", + "lazar/tia-lsp.nvim", ft = "scl", lazy = true, dependencies = { @@ -71,7 +78,7 @@ return { "saghen/blink.cmp", }, config = function() - require("scl_lsp").setup({ + require("tia_lsp").setup({ -- LSP options lsp = { on_attach = function(client, bufnr) @@ -90,19 +97,59 @@ return { ### Manual Setup ```lua -- In your init.lua or plugin file -require("scl_lsp").setup({ - server_path = "/path/to/scl_lsp/src/main.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://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 + -- ~/.config/nvim/lua/plugins/tia-mason-registry.lua + return { + url = "https://your-gitea/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. 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. + ## Configuration Options | Option | Type | Default | Description | |--------|------|---------|-------------| -| `server_path` | string | `~/dev/scl_lsp/src/main.lua` | Path to LSP server | +| `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 | @@ -185,7 +232,7 @@ The LSP automatically scans for SCL files in `data_types/` folder and extracts T ### Option 3: Generate `.plc.json` ```bash -lua /home/lazar/dev/scl_lsp/src/plc_json.lua --generate +lua ~/dev/tia-lsp/src/plc_json.lua --generate ``` ## Global Data Block Support @@ -218,40 +265,43 @@ The plugin automatically scans for `.db` files in the workspace and provides aut ### Project Structure ``` -scl_lsp/ +tia-lsp/ ├── README.md # This file ├── package.json # NPM scripts (tree-sitter) ├── Makefile # Build commands -├── grammar.js # Tree-sitter grammar -├── tree-sitter.json # Parser config -├── queries/ # Tree-sitter queries -│ ├── highlights.scm # Syntax highlighting -│ ├── indents.scm # Indentation -│ ├── folds.scm # Code folding -│ ├── locals.scm # Variable scoping -│ └── tags.scm # Navigation tags -├── src/ # LSP server -│ ├── main.lua # Main entry point -│ ├── parser.lua # Regex-based parser -│ ├── treesitter.lua # Tree-sitter integration -│ ├── json.lua # JSON encoder/decoder -│ ├── plc_json.lua # External UDT loading -│ ├── diagnostics.lua # Linter diagnostics -│ └── formatter.lua # Document formatter +├── grammar.js # Tree-sitter grammar (SCL language) +├── tree-sitter.json # Parser config +├── queries/ # Tree-sitter queries +│ └── scl/ # Per-language query dir (SCL) +│ ├── highlights.scm # Syntax highlighting +│ ├── indents.scm # Indentation +│ ├── folds.scm # Code folding +│ ├── locals.scm # Variable scoping +│ └── tags.scm # Navigation tags +├── src/ # LSP server +│ ├── main.lua # Main entry point (JSON-RPC over stdio) +│ ├── parser.lua # SCL parser (will become parser_scl.lua + dispatch) +│ ├── treesitter.lua # Tree-sitter integration +│ ├── json.lua # JSON encoder/decoder +│ ├── plc_json.lua # External UDT loading (language-agnostic) +│ ├── diagnostics.lua # Linter diagnostics +│ └── formatter.lua # Document formatter └── lua/ - └── scl_lsp/ - ├── init.lua # Main plugin setup - ├── plugins/ - │ └── scl.lua # LazyVim plugin spec - └── scl/ # Syntax/completion modules - ├── init.lua - ├── auto_prefix.lua - ├── blink_cmp_source.lua - ├── db_parser.lua - ├── fb_parser.lua - ├── udt_parser.lua - ├── variables.lua - └── workspace_types.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 @@ -294,7 +344,7 @@ The plugin uses a standalone LSP server (`src/main.lua`) that communicates via J - Completion, hover, go-to-definition - Semantic tokens -The Neovim plugin (`lua/scl_lsp/init.lua`) manages: +The Neovim plugin (`lua/tia_lsp/init.lua`) manages: - LSP client lifecycle via `vim.lsp.start()` - FileType autocmd for lazy loading - blink.cmp integration diff --git a/package.json b/package.json index 047451e..7687162 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "scl-language-server", + "name": "tia-lsp", "version": "0.1.0", - "description": "Language Server Protocol implementation for Siemens SCL", + "description": "Language Server Protocol implementation for Siemens TIA Portal languages (SCL, with planned STL/AWL, LAD, FBD support)", "main": "src/main.lua", "scripts": { "start": "lua src/main.lua", diff --git a/scl_lsp.sh b/scl_lsp.sh deleted file mode 100755 index 4361bf8..0000000 --- a/scl_lsp.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -cd /home/lazar/dev/scl_lsp -lua src/main.lua diff --git a/src/diagnostics.lua b/src/diagnostics.lua index 7d14974..7e9132b 100644 --- a/src/diagnostics.lua +++ b/src/diagnostics.lua @@ -122,7 +122,7 @@ function M.validate_diagnostics(content, workspace_types, root_dir) severity = DiagnosticSeverity.Error, message = string.format("Undefined variable: %s", var_name), code = "SCL001", - source = "scl_lsp", + source = "tia_lsp", }) end end @@ -141,7 +141,7 @@ function M.validate_diagnostics(content, workspace_types, root_dir) severity = DiagnosticSeverity.Error, message = "Variable declarations should NOT have '#' prefix in VAR section", code = "SCL002", - source = "scl_lsp", + source = "tia_lsp", }) end @@ -210,7 +210,7 @@ function M.validate_diagnostics(content, workspace_types, root_dir) severity = DiagnosticSeverity.Warning, message = string.format("Unknown data type: %s", base_type), code = "SCL003", - source = "scl_lsp", + source = "tia_lsp", }) end end @@ -229,7 +229,7 @@ function M.validate_diagnostics(content, workspace_types, root_dir) severity = DiagnosticSeverity.Error, message = "Cannot use '#' prefix for variables in VAR section", code = "SCL004", - source = "scl_lsp", + source = "tia_lsp", }) end end