refactor: rename scl_lsp → tia_lsp (project umbrella for future STL/LAD/FBD)
Rename the project from scl_lsp to tia_lsp to future-proof for additional
TIA Portal languages (STL/AWL, LAD, FBD). The 'scl' language name is kept
as-is for the SCL filetype and tree-sitter parser; future languages get
their own names (stl, lad, fbd).
Project-wide changes:
- lua/scl_lsp/ → lua/tia_lsp/ (plugin entry point)
- lua/scl/ → lua/tia/ (language modules, shared across TIA langs)
- require('scl.*') → require('tia.*')
- require('scl_lsp') → require('tia_lsp')
- LSP client name: 'scl_lsp' → 'tia_lsp'
- Diagnostic source: 'scl_lsp' → 'tia_lsp' (src/diagnostics.lua)
- package.json name: 'scl-language-server' → 'tia-lsp'
lua/tia_lsp/init.lua:
- Add vim.fn.exepath('tia-lsp') detection so the plugin prefers the
mason-installed executable and falls back to { 'lua', server_path }
for manual installs.
- Add opts.ts_parser_url to configure the tree-sitter parser source.
- Remove hardcoded ~/dev/scl_lsp paths in favor of ~/dev/tia-lsp and
the new ts_parser_url option.
Cleanup:
- Delete lua/tia/init.lua (dead code, unreferenced duplicate of tia_lsp).
- Delete scl_lsp.sh (replaced by the mason-installed bin/tia-lsp wrapper).
- Update README.md and AGENTS.md to reflect the two-repo split
(tia-lsp server + tia-lsp.nvim plugin) and document Mason installation.
Unchanged (intentional):
- grammar.js, src/grammar.json, src/parser.c: tree-sitter language name
remains 'scl' (it's the language, not the project).
- Filetype patterns ('scl', 'udt', 'db') in autocmds.
- :SCL* command names (per-filetype prefix; future :STL* etc.).
Tests pass with the same pre-existing failures as before the rename
(formatter VAR_INPUT test, udt_parser line 199 const-variable bug,
plc_json reference-project-missing). No new failures introduced.
This commit is contained in:
@@ -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
|
## Build/Lint/Test Commands
|
||||||
|
|
||||||
@@ -17,7 +20,7 @@ tree-sitter parse <file.scl> # Parse single SCL file
|
|||||||
|
|
||||||
# Lua Linting
|
# Lua Linting
|
||||||
luacheck src/ # Lint LSP server code
|
luacheck src/ # Lint LSP server code
|
||||||
luacheck lua/scl/ # Lint plugin modules
|
luacheck lua/tia/ # Lint plugin modules
|
||||||
|
|
||||||
# Unit & Integration Tests
|
# Unit & Integration Tests
|
||||||
lua test/run_tests.lua # Run all tests
|
lua test/run_tests.lua # Run all tests
|
||||||
@@ -36,18 +39,17 @@ lua test/test_integration.lua # Run integration tests
|
|||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
scl_lsp/
|
tia-lsp/
|
||||||
├── src/ # LSP server (uses dofile)
|
├── src/ # LSP server (uses dofile)
|
||||||
│ ├── main.lua # Entry point, JSON-RPC protocol
|
│ ├── main.lua # Entry point, JSON-RPC protocol
|
||||||
│ ├── parser.lua # SCL parser
|
│ ├── parser.lua # SCL parser (will become parser_scl.lua + dispatch)
|
||||||
│ ├── diagnostics.lua # Linter
|
│ ├── diagnostics.lua # Linter (source = "tia_lsp")
|
||||||
│ ├── formatter.lua # Document formatter
|
│ ├── formatter.lua # Document formatter
|
||||||
│ ├── treesitter.lua # Tree-sitter integration
|
│ ├── 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
|
│ ├── builtin_instructions.lua # TIA Portal built-in types
|
||||||
│ └── json.lua # JSON encoder/decoder
|
│ └── json.lua # JSON encoder/decoder
|
||||||
├── lua/scl/ # Neovim plugin (uses require)
|
├── lua/tia/ # Neovim plugin (uses require)
|
||||||
│ ├── init.lua # Main plugin setup
|
|
||||||
│ ├── 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
|
||||||
@@ -56,13 +58,24 @@ scl_lsp/
|
|||||||
│ ├── multiline_params.lua
|
│ ├── multiline_params.lua
|
||||||
│ ├── auto_prefix.lua
|
│ ├── auto_prefix.lua
|
||||||
│ └── attr_toggle.lua
|
│ └── attr_toggle.lua
|
||||||
├── queries/ # Tree-sitter queries
|
├── lua/tia_lsp/ # Plugin entry point
|
||||||
└── grammar.js # Tree-sitter grammar
|
│ ├── 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
|
## 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
|
```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
|
||||||
@@ -93,12 +106,13 @@ return M
|
|||||||
- Booleans: prefix with `is_`, `has_` (e.g., `is_udt`, `has_members`)
|
- Booleans: prefix with `is_`, `has_` (e.g., `is_udt`, `has_members`)
|
||||||
|
|
||||||
### Imports
|
### 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")`
|
- `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/scl/`: 2 spaces indentation
|
- `lua/tia/` and `lua/tia_lsp/`: 2 spaces indentation
|
||||||
- `src/`: tab-based indentation
|
- `src/`: tab-based indentation
|
||||||
- Max line length: 120 characters
|
- Max line length: 120 characters
|
||||||
- No trailing whitespace
|
- No trailing whitespace
|
||||||
|
|||||||
@@ -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
|
## Features
|
||||||
|
|
||||||
@@ -58,11 +63,13 @@ A comprehensive Neovim/LazyVim plugin providing **Language Server Protocol (LSP)
|
|||||||
|
|
||||||
## 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.
|
||||||
|
|
||||||
### LazyVim
|
### LazyVim
|
||||||
```lua
|
```lua
|
||||||
-- ~/.config/nvim/lua/plugins/scl.lua
|
-- ~/.config/nvim/lua/plugins/tia-lsp.lua
|
||||||
return {
|
return {
|
||||||
"lazar/scl_lsp",
|
"lazar/tia-lsp.nvim",
|
||||||
ft = "scl",
|
ft = "scl",
|
||||||
lazy = true,
|
lazy = true,
|
||||||
dependencies = {
|
dependencies = {
|
||||||
@@ -71,7 +78,7 @@ return {
|
|||||||
"saghen/blink.cmp",
|
"saghen/blink.cmp",
|
||||||
},
|
},
|
||||||
config = function()
|
config = function()
|
||||||
require("scl_lsp").setup({
|
require("tia_lsp").setup({
|
||||||
-- LSP options
|
-- LSP options
|
||||||
lsp = {
|
lsp = {
|
||||||
on_attach = function(client, bufnr)
|
on_attach = function(client, bufnr)
|
||||||
@@ -90,19 +97,59 @@ return {
|
|||||||
### Manual Setup
|
### Manual Setup
|
||||||
```lua
|
```lua
|
||||||
-- In your init.lua or plugin file
|
-- In your init.lua or plugin file
|
||||||
require("scl_lsp").setup({
|
require("tia_lsp").setup({
|
||||||
server_path = "/path/to/scl_lsp/src/main.lua",
|
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,
|
cmp = true,
|
||||||
workspace_types = true,
|
workspace_types = true,
|
||||||
auto_prefix = 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
|
## Configuration Options
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
| 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.on_attach` | function | `nil` | Custom LSP attach callback |
|
||||||
| `lsp.formatting` | boolean | `false` | Enable format command |
|
| `lsp.formatting` | boolean | `false` | Enable format command |
|
||||||
| `cmp` | boolean | `true` | Enable blink.cmp integration |
|
| `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`
|
### Option 3: Generate `.plc.json`
|
||||||
```bash
|
```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
|
## Global Data Block Support
|
||||||
@@ -218,37 +265,40 @@ The plugin automatically scans for `.db` files in the workspace and provides aut
|
|||||||
### Project Structure
|
### Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
scl_lsp/
|
tia-lsp/
|
||||||
├── README.md # This file
|
├── README.md # This file
|
||||||
├── package.json # NPM scripts (tree-sitter)
|
├── package.json # NPM scripts (tree-sitter)
|
||||||
├── Makefile # Build commands
|
├── Makefile # Build commands
|
||||||
├── grammar.js # Tree-sitter grammar
|
├── grammar.js # Tree-sitter grammar (SCL language)
|
||||||
├── tree-sitter.json # Parser config
|
├── tree-sitter.json # Parser config
|
||||||
├── queries/ # Tree-sitter queries
|
├── queries/ # Tree-sitter queries
|
||||||
|
│ └── scl/ # Per-language query dir (SCL)
|
||||||
│ ├── 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
|
├── src/ # LSP server
|
||||||
│ ├── main.lua # Main entry point
|
│ ├── main.lua # Main entry point (JSON-RPC over stdio)
|
||||||
│ ├── parser.lua # Regex-based parser
|
│ ├── parser.lua # SCL parser (will become parser_scl.lua + dispatch)
|
||||||
│ ├── treesitter.lua # Tree-sitter integration
|
│ ├── treesitter.lua # Tree-sitter integration
|
||||||
│ ├── json.lua # JSON encoder/decoder
|
│ ├── json.lua # JSON encoder/decoder
|
||||||
│ ├── plc_json.lua # External UDT loading
|
│ ├── plc_json.lua # External UDT loading (language-agnostic)
|
||||||
│ ├── diagnostics.lua # Linter diagnostics
|
│ ├── diagnostics.lua # Linter diagnostics
|
||||||
│ └── formatter.lua # Document formatter
|
│ └── formatter.lua # Document formatter
|
||||||
└── lua/
|
└── lua/
|
||||||
└── scl_lsp/
|
├── tia_lsp/ # Neovim plugin
|
||||||
├── init.lua # Main plugin setup
|
│ ├── init.lua # Main plugin setup
|
||||||
├── plugins/
|
│ └── plugins/
|
||||||
│ └── scl.lua # LazyVim plugin spec
|
│ └── scl.lua # LazyVim plugin spec (SCL filetype)
|
||||||
└── scl/ # Syntax/completion modules
|
└── tia/ # Language modules (shared across TIA languages)
|
||||||
├── init.lua
|
|
||||||
├── auto_prefix.lua
|
├── auto_prefix.lua
|
||||||
|
├── attr_toggle.lua
|
||||||
├── blink_cmp_source.lua
|
├── blink_cmp_source.lua
|
||||||
|
├── builtin_instructions.lua
|
||||||
├── db_parser.lua
|
├── db_parser.lua
|
||||||
├── fb_parser.lua
|
├── fb_parser.lua
|
||||||
|
├── multiline_params.lua
|
||||||
├── udt_parser.lua
|
├── udt_parser.lua
|
||||||
├── variables.lua
|
├── variables.lua
|
||||||
└── workspace_types.lua
|
└── workspace_types.lua
|
||||||
@@ -294,7 +344,7 @@ The plugin uses a standalone LSP server (`src/main.lua`) that communicates via J
|
|||||||
- Completion, hover, go-to-definition
|
- Completion, hover, go-to-definition
|
||||||
- Semantic tokens
|
- 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()`
|
- LSP client lifecycle via `vim.lsp.start()`
|
||||||
- FileType autocmd for lazy loading
|
- FileType autocmd for lazy loading
|
||||||
- blink.cmp integration
|
- blink.cmp integration
|
||||||
|
|||||||
@@ -1,266 +0,0 @@
|
|||||||
-- SCL (Siemens Structured Control Language) support for Neovim
|
|
||||||
-- Main setup module
|
|
||||||
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
function M.setup(opts)
|
|
||||||
opts = opts or {}
|
|
||||||
|
|
||||||
-- Add plugin to runtime path
|
|
||||||
vim.opt.runtimepath:prepend(vim.fn.stdpath("data") .. "/lazy/scl_lsp")
|
|
||||||
|
|
||||||
M.create_commands()
|
|
||||||
|
|
||||||
-- Treesitter setup for Neovim 0.11+
|
|
||||||
local function setup_treesitter()
|
|
||||||
local lang = vim.treesitter.language
|
|
||||||
local parser_path = "/home/lazar/dev/scl_lsp/src/parser.so"
|
|
||||||
|
|
||||||
-- Register the SCL language
|
|
||||||
lang.add("scl", {
|
|
||||||
path = parser_path,
|
|
||||||
filetype = "scl",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
pcall(setup_treesitter)
|
|
||||||
|
|
||||||
-- Install queries
|
|
||||||
local queries_src = "/home/lazar/dev/scl_lsp/queries/highlights.scm"
|
|
||||||
local queries_dest = vim.fn.stdpath("data") .. "/site/queries/scl/highlights.scm"
|
|
||||||
if vim.fn.filereadable(queries_src) == 1 then
|
|
||||||
vim.fn.mkdir(vim.fn.stdpath("data") .. "/site/queries/scl", "p")
|
|
||||||
vim.fn.writefile(vim.fn.readfile(queries_src), queries_dest)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Enable treesitter manually for SCL files - bypass LazyVim
|
|
||||||
vim.api.nvim_create_autocmd("FileType", {
|
|
||||||
pattern = "scl",
|
|
||||||
callback = function(args)
|
|
||||||
vim.treesitter.start(args.buf, "scl")
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Register blink.cmp source
|
|
||||||
if opts.cmp then
|
|
||||||
local function try_register()
|
|
||||||
local cmp_ok, cmp = pcall(require, "blink.cmp")
|
|
||||||
if cmp_ok and cmp and cmp.add_source_provider then
|
|
||||||
local source_config = {
|
|
||||||
name = "scl",
|
|
||||||
module = "scl.blink_cmp_source",
|
|
||||||
score_offset = 100,
|
|
||||||
}
|
|
||||||
pcall(cmp.add_source_provider, "scl", source_config)
|
|
||||||
pcall(cmp.add_filetype_source, "scl", "scl")
|
|
||||||
|
|
||||||
-- Create autocmd to trigger completion on ( for SCL files
|
|
||||||
local augroup = vim.api.nvim_create_augroup("SCLCompletionTrigger", { clear = true })
|
|
||||||
vim.api.nvim_create_autocmd("TextChangedI", {
|
|
||||||
group = augroup,
|
|
||||||
pattern = "*.scl",
|
|
||||||
callback = function(args)
|
|
||||||
local line = vim.api.nvim_get_current_line()
|
|
||||||
local col = vim.api.nvim_win_get_cursor(0)[2]
|
|
||||||
-- Check if the character before cursor is (
|
|
||||||
if col > 0 and line:sub(col, col) == "(" then
|
|
||||||
-- Check if there's a known FB variable before the (
|
|
||||||
local before_paren = line:sub(1, col - 1):match("[%w_]+%s*$")
|
|
||||||
if before_paren then
|
|
||||||
vim.schedule(function()
|
|
||||||
pcall(require("blink.cmp").show, { providers = { "scl" } })
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.notify("SCL: blink.cmp source registered", vim.log.levels.INFO)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if not try_register() then
|
|
||||||
vim.api.nvim_create_autocmd("BufEnter", {
|
|
||||||
pattern = "*.scl",
|
|
||||||
once = true,
|
|
||||||
callback = try_register,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Workspace type scanner
|
|
||||||
if opts.workspace_types ~= false then
|
|
||||||
local workspace_types = require("scl.workspace_types")
|
|
||||||
workspace_types.setup({
|
|
||||||
project_root = opts.project_root,
|
|
||||||
library_paths = opts.library_paths,
|
|
||||||
debug = opts.debug,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Auto-prefixing (enabled by default)
|
|
||||||
M.setup_auto_prefix()
|
|
||||||
|
|
||||||
-- Multiline parameters feature
|
|
||||||
M.setup_multiline_params()
|
|
||||||
|
|
||||||
-- Attribute block toggle feature
|
|
||||||
M.setup_attr_toggle()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.setup_multiline_params()
|
|
||||||
local mp = require("scl.multiline_params")
|
|
||||||
|
|
||||||
vim.api.nvim_create_user_command("SCLMultilineParams", function()
|
|
||||||
mp.fill_multiline_params()
|
|
||||||
end, {})
|
|
||||||
|
|
||||||
vim.keymap.set("i", "<Space>mp", function()
|
|
||||||
mp.fill_multiline_params()
|
|
||||||
end, { noremap = true, silent = true, desc = "SCL: Fill multiline parameters" })
|
|
||||||
|
|
||||||
vim.keymap.set("i", "<Tab>", function()
|
|
||||||
local ft = vim.bo.filetype
|
|
||||||
if ft == "scl" or ft == "udt" then
|
|
||||||
if mp.jump_to_next_param() then
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return "<Tab>"
|
|
||||||
end, { noremap = true, silent = true, desc = "SCL: Jump to next param or Tab" })
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.setup_auto_prefix()
|
|
||||||
require("scl.auto_prefix").setup()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.prefix_current_word()
|
|
||||||
require("scl.auto_prefix").prefix_word()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.show_variables()
|
|
||||||
local vars = require("scl.variables").extract_variables()
|
|
||||||
if #vars == 0 then
|
|
||||||
vim.notify("No local variables found", vim.log.levels.INFO)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local lines = { "Local Variables:", "---" }
|
|
||||||
for _, var in ipairs(vars) do
|
|
||||||
local type_info = var.type and (" : " .. var.type) or ""
|
|
||||||
table.insert(lines, "#" .. var.name .. type_info)
|
|
||||||
end
|
|
||||||
vim.notify(table.concat(lines, "\n"), vim.log.levels.INFO)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.show_workspace_types()
|
|
||||||
local ws = require("scl.workspace_types")
|
|
||||||
local udt = require("scl.udt_parser")
|
|
||||||
local fb = require("scl.fb_parser")
|
|
||||||
local db = require("scl.db_parser")
|
|
||||||
local root = ws.get_project_root()
|
|
||||||
local udt_names = udt.get_all_udt_names()
|
|
||||||
local fb_names = fb.get_all_fb_names()
|
|
||||||
local db_names = db.get_all_db_names()
|
|
||||||
if not root then
|
|
||||||
vim.notify("No project root detected", vim.log.levels.WARN)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
vim.notify("Project: " .. root .. "\nUDTs: " .. #udt_names .. "\nFBs/Functions: " .. #fb_names .. "\nGlobal DBs: " .. #db_names, vim.log.levels.INFO)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.rescan_workspace_types()
|
|
||||||
local ws = require("scl.workspace_types")
|
|
||||||
local udt = require("scl.udt_parser")
|
|
||||||
local fb = require("scl.fb_parser")
|
|
||||||
local db = require("scl.db_parser")
|
|
||||||
udt.clear_cache()
|
|
||||||
fb.clear_cache()
|
|
||||||
db.clear_cache()
|
|
||||||
ws.clear_root_cache()
|
|
||||||
local udt_result = ws.scan_project_udts()
|
|
||||||
local fb_result = ws.scan_project_fbs()
|
|
||||||
local db_result = ws.scan_project_dbs()
|
|
||||||
vim.notify("Scanned UDTs: " .. udt_result.parsed_count .. "/" .. udt_result.total_files ..
|
|
||||||
", FBs: " .. fb_result.parsed_count .. "/" .. fb_result.total_files ..
|
|
||||||
", DBs: " .. db_result.parsed_count .. "/" .. db_result.total_files, vim.log.levels.INFO)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.create_commands()
|
|
||||||
vim.api.nvim_create_user_command("SCLShowVariables", function() M.show_variables() end, {})
|
|
||||||
vim.api.nvim_create_user_command("SCLShowWorkspaceTypes", function() M.show_workspace_types() end, {})
|
|
||||||
vim.api.nvim_create_user_command("SCLRescanWorkspaceTypes", function() M.rescan_workspace_types() end, {})
|
|
||||||
|
|
||||||
-- Attribute toggle commands
|
|
||||||
vim.api.nvim_create_user_command("SCLToggleAttrBlock", function()
|
|
||||||
local ok, at = pcall(require, "scl.attr_toggle")
|
|
||||||
if ok then
|
|
||||||
at.toggle_attr_block()
|
|
||||||
else
|
|
||||||
vim.notify("SCL: Failed to load attr_toggle module", vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
end, {})
|
|
||||||
vim.api.nvim_create_user_command("SCLExpandAllAttrBlocks", function()
|
|
||||||
local ok, at = pcall(require, "scl.attr_toggle")
|
|
||||||
if ok then
|
|
||||||
at.expand_all_attr_blocks()
|
|
||||||
else
|
|
||||||
vim.notify("SCL: Failed to load attr_toggle module", vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
end, {})
|
|
||||||
vim.api.nvim_create_user_command("SCLCollapseAllAttrBlocks", function()
|
|
||||||
local ok, at = pcall(require, "scl.attr_toggle")
|
|
||||||
if ok then
|
|
||||||
at.collapse_all_attr_blocks()
|
|
||||||
else
|
|
||||||
vim.notify("SCL: Failed to load attr_toggle module", vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
end, {})
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.setup_attr_toggle()
|
|
||||||
local at = require("scl.attr_toggle")
|
|
||||||
at.setup()
|
|
||||||
|
|
||||||
local function setup_buffer_keymaps(bufnr)
|
|
||||||
local opts = { buffer = bufnr, silent = true }
|
|
||||||
local ft = vim.bo[bufnr].filetype
|
|
||||||
|
|
||||||
if ft ~= "scl" and ft ~= "udt" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Toggle individual attribute block under cursor
|
|
||||||
vim.keymap.set({ "n", "i" }, "<Leader>xa", function()
|
|
||||||
at.toggle_attr_block()
|
|
||||||
end, vim.tbl_extend("force", opts, { desc = "SCL: Toggle attribute block" }))
|
|
||||||
|
|
||||||
-- Expand all collapsed blocks
|
|
||||||
vim.keymap.set("n", "<Leader>xae", function()
|
|
||||||
at.expand_all_attr_blocks()
|
|
||||||
end, vim.tbl_extend("force", opts, { desc = "SCL: Expand all attribute blocks" }))
|
|
||||||
|
|
||||||
-- Collapse all attribute blocks
|
|
||||||
vim.keymap.set("n", "<Leader>xac", function()
|
|
||||||
at.collapse_all_attr_blocks()
|
|
||||||
end, vim.tbl_extend("force", opts, { desc = "SCL: Collapse all attribute blocks" }))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set up keybindings for SCL files only
|
|
||||||
vim.api.nvim_create_autocmd("FileType", {
|
|
||||||
pattern = { "scl", "udt" },
|
|
||||||
callback = function(args)
|
|
||||||
setup_buffer_keymaps(args.buf)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Also check if there are already SCL buffers open
|
|
||||||
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
|
|
||||||
if vim.api.nvim_buf_is_loaded(bufnr) then
|
|
||||||
setup_buffer_keymaps(bufnr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@@ -71,7 +71,7 @@ local function get_local_variables()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local scl_vars = require("scl.variables")
|
local scl_vars = require("tia.variables")
|
||||||
local vars = scl_vars.extract_variables(bufnr)
|
local vars = scl_vars.extract_variables(bufnr)
|
||||||
|
|
||||||
var_cache = {}
|
var_cache = {}
|
||||||
@@ -545,7 +545,7 @@ function M.setup()
|
|||||||
local before_semicolon = line:sub(1, cursor_col - 1)
|
local before_semicolon = line:sub(1, cursor_col - 1)
|
||||||
local type_name = before_semicolon:match(":%s*([%w_]+)%s*$")
|
local type_name = before_semicolon:match(":%s*([%w_]+)%s*$")
|
||||||
if type_name then
|
if type_name then
|
||||||
local udt_parser = require("scl.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
local is_udt = udt_parser.is_udt_type(type_name)
|
local is_udt = udt_parser.is_udt_type(type_name)
|
||||||
if is_udt and not before_semicolon:match(":%s*\"" .. type_name .. "\"") then
|
if is_udt and not before_semicolon:match(":%s*\"" .. type_name .. "\"") then
|
||||||
local new_before = before_semicolon:gsub(":%s*" .. type_name .. "%s*$", ':"' .. type_name .. '"')
|
local new_before = before_semicolon:gsub(":%s*" .. type_name .. "%s*$", ':"' .. type_name .. '"')
|
||||||
@@ -104,7 +104,7 @@ end
|
|||||||
|
|
||||||
-- Get all known local variables
|
-- Get all known local variables
|
||||||
local function get_known_variables(bufnr)
|
local function get_known_variables(bufnr)
|
||||||
local scl_vars = require("scl.variables")
|
local scl_vars = require("tia.variables")
|
||||||
local variables = scl_vars.extract_variables(bufnr)
|
local variables = scl_vars.extract_variables(bufnr)
|
||||||
local known = {}
|
local known = {}
|
||||||
for _, v in ipairs(variables) do
|
for _, v in ipairs(variables) do
|
||||||
@@ -115,12 +115,12 @@ end
|
|||||||
|
|
||||||
-- Check if a type is an FB/Function and get its interface
|
-- Check if a type is an FB/Function and get its interface
|
||||||
local function get_fb_interface(var_type)
|
local function get_fb_interface(var_type)
|
||||||
local fb_parser = require("scl.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
local clean_type = var_type:gsub('"', '')
|
local clean_type = var_type:gsub('"', '')
|
||||||
|
|
||||||
-- If cache is empty, try to scan workspace for FBs
|
-- If cache is empty, try to scan workspace for FBs
|
||||||
if fb_parser.get_cache_count() == 0 then
|
if fb_parser.get_cache_count() == 0 then
|
||||||
local ws = require("scl.workspace_types")
|
local ws = require("tia.workspace_types")
|
||||||
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -129,12 +129,12 @@ end
|
|||||||
|
|
||||||
-- Check if a type is an FB/Function
|
-- Check if a type is an FB/Function
|
||||||
local function is_fb_type(var_type)
|
local function is_fb_type(var_type)
|
||||||
local fb_parser = require("scl.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
local clean_type = var_type:gsub('"', '')
|
local clean_type = var_type:gsub('"', '')
|
||||||
|
|
||||||
-- If cache is empty, try to scan workspace for FBs
|
-- If cache is empty, try to scan workspace for FBs
|
||||||
if fb_parser.get_cache_count() == 0 then
|
if fb_parser.get_cache_count() == 0 then
|
||||||
local ws = require("scl.workspace_types")
|
local ws = require("tia.workspace_types")
|
||||||
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ function M:get_completions(context, callback)
|
|||||||
local after_closed_db = active_portion:match('"([%w_]+)"%s*%.') ~= nil
|
local after_closed_db = active_portion:match('"([%w_]+)"%s*%.') ~= nil
|
||||||
|
|
||||||
if db_member_pattern or after_closed_db then
|
if db_member_pattern or after_closed_db then
|
||||||
local db_parser = require("scl.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
local db_name = active_portion:match('"([%w_]+)"')
|
local db_name = active_portion:match('"([%w_]+)"')
|
||||||
|
|
||||||
if db_name then
|
if db_name then
|
||||||
@@ -271,7 +271,7 @@ function M:get_completions(context, callback)
|
|||||||
resolved_path = part
|
resolved_path = part
|
||||||
if found_member.is_udt then
|
if found_member.is_udt then
|
||||||
-- This member is a UDT type, continue resolving
|
-- This member is a UDT type, continue resolving
|
||||||
local udt_parser = require("scl.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
current_members = udt_parser.get_udt_members(found_member.type)
|
current_members = udt_parser.get_udt_members(found_member.type)
|
||||||
current_type = found_member.type
|
current_type = found_member.type
|
||||||
else
|
else
|
||||||
@@ -306,7 +306,7 @@ function M:get_completions(context, callback)
|
|||||||
|
|
||||||
-- Get members to show (either from DB or from resolved UDT type)
|
-- Get members to show (either from DB or from resolved UDT type)
|
||||||
if current_type then
|
if current_type then
|
||||||
local udt_parser = require("scl.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
current_members = udt_parser.get_udt_members(current_type)
|
current_members = udt_parser.get_udt_members(current_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -335,11 +335,11 @@ function M:get_completions(context, callback)
|
|||||||
|
|
||||||
-- Check for just " pattern (start of DB name completion or partial DB name)
|
-- Check for just " pattern (start of DB name completion or partial DB name)
|
||||||
if is_in_db_name or is_partial_db or is_after_colon_quote then
|
if is_in_db_name or is_partial_db or is_after_colon_quote then
|
||||||
local db_parser = require("scl.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
|
|
||||||
-- If cache is empty, try to scan workspace for DBs
|
-- If cache is empty, try to scan workspace for DBs
|
||||||
if db_parser.get_cache_count() == 0 then
|
if db_parser.get_cache_count() == 0 then
|
||||||
local ws = require("scl.workspace_types")
|
local ws = require("tia.workspace_types")
|
||||||
ws.scan_project_dbs(bufnr)
|
ws.scan_project_dbs(bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -376,7 +376,7 @@ function M:get_completions(context, callback)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local scl_vars = require("scl.variables")
|
local scl_vars = require("tia.variables")
|
||||||
local variables = scl_vars.extract_variables(bufnr)
|
local variables = scl_vars.extract_variables(bufnr)
|
||||||
|
|
||||||
-- Get all known local variables and their types
|
-- Get all known local variables and their types
|
||||||
@@ -747,8 +747,8 @@ function M:get_completions(context, callback)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
local udt_parser = require("scl.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
local db_parser = require("scl.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
|
|
||||||
-- No member access = show local variables, UDT types, and basic types
|
-- No member access = show local variables, UDT types, and basic types
|
||||||
if not base_var then
|
if not base_var then
|
||||||
@@ -779,9 +779,9 @@ function M:get_completions(context, callback)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Add Global DB names (without quotes - will be wrapped in resolve)
|
-- Add Global DB names (without quotes - will be wrapped in resolve)
|
||||||
local db_parser = require("scl.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
if db_parser.get_cache_count() == 0 then
|
if db_parser.get_cache_count() == 0 then
|
||||||
local ws = require("scl.workspace_types")
|
local ws = require("tia.workspace_types")
|
||||||
ws.scan_project_dbs(bufnr)
|
ws.scan_project_dbs(bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ local M = {}
|
|||||||
--- @param var_type string The FB type name
|
--- @param var_type string The FB type name
|
||||||
--- @return table|nil Interface with inputs, outputs, inouts
|
--- @return table|nil Interface with inputs, outputs, inouts
|
||||||
local function get_builtin_interface(var_type)
|
local function get_builtin_interface(var_type)
|
||||||
local builtin = require("scl.builtin_instructions")
|
local builtin = require("tia.builtin_instructions")
|
||||||
local clean_type = var_type:gsub('"', ''):gsub("#", ""):upper()
|
local clean_type = var_type:gsub('"', ''):gsub("#", ""):upper()
|
||||||
local params_info = builtin.get_parameters(clean_type)
|
local params_info = builtin.get_parameters(clean_type)
|
||||||
if params_info then
|
if params_info then
|
||||||
@@ -24,11 +24,11 @@ end
|
|||||||
--- @param var_type string The FB type name
|
--- @param var_type string The FB type name
|
||||||
--- @return table|nil Interface with inputs, outputs, inouts
|
--- @return table|nil Interface with inputs, outputs, inouts
|
||||||
local function get_fb_interface(var_type)
|
local function get_fb_interface(var_type)
|
||||||
local fb_parser = require("scl.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
local clean_type = var_type:gsub('"', '')
|
local clean_type = var_type:gsub('"', '')
|
||||||
|
|
||||||
if fb_parser.get_cache_count() == 0 then
|
if fb_parser.get_cache_count() == 0 then
|
||||||
local ws = require("scl.workspace_types")
|
local ws = require("tia.workspace_types")
|
||||||
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -39,11 +39,11 @@ end
|
|||||||
--- @param var_type string The type name to check
|
--- @param var_type string The type name to check
|
||||||
--- @return boolean True if type is an FB
|
--- @return boolean True if type is an FB
|
||||||
local function is_fb_type(var_type)
|
local function is_fb_type(var_type)
|
||||||
local fb_parser = require("scl.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
local clean_type = var_type:gsub('"', '')
|
local clean_type = var_type:gsub('"', '')
|
||||||
|
|
||||||
if fb_parser.get_cache_count() == 0 then
|
if fb_parser.get_cache_count() == 0 then
|
||||||
local ws = require("scl.workspace_types")
|
local ws = require("tia.workspace_types")
|
||||||
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ end
|
|||||||
--- @param bufnr number Buffer number
|
--- @param bufnr number Buffer number
|
||||||
--- @return table Set of variable names
|
--- @return table Set of variable names
|
||||||
local function get_known_variables(bufnr)
|
local function get_known_variables(bufnr)
|
||||||
local scl_vars = require("scl.variables")
|
local scl_vars = require("tia.variables")
|
||||||
local variables = scl_vars.extract_variables(bufnr)
|
local variables = scl_vars.extract_variables(bufnr)
|
||||||
local known = {}
|
local known = {}
|
||||||
for _, v in ipairs(variables) do
|
for _, v in ipairs(variables) do
|
||||||
@@ -68,7 +68,7 @@ end
|
|||||||
--- @param bufnr number Buffer number
|
--- @param bufnr number Buffer number
|
||||||
--- @return string|nil Variable type, or nil if not found
|
--- @return string|nil Variable type, or nil if not found
|
||||||
local function get_variable_type(var_name, bufnr)
|
local function get_variable_type(var_name, bufnr)
|
||||||
local scl_vars = require("scl.variables")
|
local scl_vars = require("tia.variables")
|
||||||
return scl_vars.get_variable_type(var_name, bufnr)
|
return scl_vars.get_variable_type(var_name, bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ function M.fill_multiline_params()
|
|||||||
local line_before_cursor = line:sub(1, col)
|
local line_before_cursor = line:sub(1, col)
|
||||||
|
|
||||||
local known_vars = get_known_variables(bufnr)
|
local known_vars = get_known_variables(bufnr)
|
||||||
local builtin = require("scl.builtin_instructions")
|
local builtin = require("tia.builtin_instructions")
|
||||||
|
|
||||||
local fb_name = nil
|
local fb_name = nil
|
||||||
local fb_prefix = ""
|
local fb_prefix = ""
|
||||||
@@ -361,7 +361,7 @@ end
|
|||||||
|
|
||||||
-- Parse all UDT files in project and libraries
|
-- Parse all UDT files in project and libraries
|
||||||
function M.scan_project_udts(bufnr)
|
function M.scan_project_udts(bufnr)
|
||||||
local udt_parser = require("scl.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
local files = M.get_project_udt_files(bufnr)
|
local files = M.get_project_udt_files(bufnr)
|
||||||
local lib_files = M.get_library_udt_files()
|
local lib_files = M.get_library_udt_files()
|
||||||
|
|
||||||
@@ -391,7 +391,7 @@ end
|
|||||||
|
|
||||||
-- Parse all FB/Function files in project and libraries
|
-- Parse all FB/Function files in project and libraries
|
||||||
function M.scan_project_fbs(bufnr)
|
function M.scan_project_fbs(bufnr)
|
||||||
local fb_parser = require("scl.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
local files = M.get_project_fb_files(bufnr)
|
local files = M.get_project_fb_files(bufnr)
|
||||||
local lib_files = M.get_library_fb_files()
|
local lib_files = M.get_library_fb_files()
|
||||||
|
|
||||||
@@ -421,7 +421,7 @@ end
|
|||||||
|
|
||||||
-- Parse all Global DB files in project and libraries
|
-- Parse all Global DB files in project and libraries
|
||||||
function M.scan_project_dbs(bufnr)
|
function M.scan_project_dbs(bufnr)
|
||||||
local db_parser = require("scl.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
local files = M.get_project_db_files(bufnr)
|
local files = M.get_project_db_files(bufnr)
|
||||||
local lib_files = M.get_library_db_files()
|
local lib_files = M.get_library_db_files()
|
||||||
|
|
||||||
@@ -486,9 +486,9 @@ function M.setup(opts)
|
|||||||
pattern = "*.scl",
|
pattern = "*.scl",
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
-- Clear cache and rescan
|
-- Clear cache and rescan
|
||||||
local udt_parser = require("scl.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
local fb_parser = require("scl.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
local db_parser = require("scl.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
udt_parser.clear_cache()
|
udt_parser.clear_cache()
|
||||||
fb_parser.clear_cache()
|
fb_parser.clear_cache()
|
||||||
db_parser.clear_cache()
|
db_parser.clear_cache()
|
||||||
@@ -506,7 +506,7 @@ function M.setup(opts)
|
|||||||
pattern = "*.udt",
|
pattern = "*.udt",
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
-- Re-parse the changed UDT file
|
-- Re-parse the changed UDT file
|
||||||
local udt_parser = require("scl.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
udt_parser.parse_udt_file(args.file)
|
udt_parser.parse_udt_file(args.file)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
@@ -517,7 +517,7 @@ function M.setup(opts)
|
|||||||
pattern = "*.scl",
|
pattern = "*.scl",
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
-- Re-parse the changed FB file
|
-- Re-parse the changed FB file
|
||||||
local fb_parser = require("scl.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
fb_parser.parse_fb_file(args.file)
|
fb_parser.parse_fb_file(args.file)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
@@ -527,7 +527,7 @@ function M.setup(opts)
|
|||||||
group = augroup,
|
group = augroup,
|
||||||
pattern = "*.db",
|
pattern = "*.db",
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
local db_parser = require("scl.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
db_parser.parse_db_file(args.file)
|
db_parser.parse_db_file(args.file)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
@@ -2,7 +2,7 @@ local M = {}
|
|||||||
|
|
||||||
local function setup_package_path()
|
local function setup_package_path()
|
||||||
local plugin_path = debug.getinfo(1, "S").source:gsub("^@", ""):match("(.*/)") or ""
|
local plugin_path = debug.getinfo(1, "S").source:gsub("^@", ""):match("(.*/)") or ""
|
||||||
plugin_path = plugin_path:gsub("/lua/scl_lsp", "")
|
plugin_path = plugin_path:gsub("/lua/tia_lsp", "")
|
||||||
local lua_path = plugin_path .. "/lua"
|
local lua_path = plugin_path .. "/lua"
|
||||||
if not package.path:match(lua_path) then
|
if not package.path:match(lua_path) then
|
||||||
package.path = lua_path .. "/?.lua;" .. package.path
|
package.path = lua_path .. "/?.lua;" .. package.path
|
||||||
@@ -13,7 +13,7 @@ function M.setup(opts)
|
|||||||
setup_package_path()
|
setup_package_path()
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
|
|
||||||
local server_path = opts.server_path or vim.fn.expand("~/dev/scl_lsp/src/main.lua")
|
local server_path = opts.server_path or vim.fn.expand("~/dev/tia-lsp/src/main.lua")
|
||||||
|
|
||||||
local root_patterns = { ".git", "data_types", "plc.data.json" }
|
local root_patterns = { ".git", "data_types", "plc.data.json" }
|
||||||
|
|
||||||
@@ -24,9 +24,15 @@ function M.setup(opts)
|
|||||||
local root_dir = vim.fs.root(fname, root_patterns)
|
local root_dir = vim.fs.root(fname, root_patterns)
|
||||||
or vim.fn.fnamemodify(fname, ":p:h")
|
or vim.fn.fnamemodify(fname, ":p:h")
|
||||||
|
|
||||||
|
-- Prefer the mason-installed executable (vim.fn.exepath resolves on each
|
||||||
|
-- buffer enter, so a :MasonInstall mid-session is picked up without restart).
|
||||||
|
-- Fall back to running the server via `lua <server_path>` for manual installs.
|
||||||
|
local tia_bin = vim.fn.exepath("tia-lsp")
|
||||||
|
local cmd = tia_bin ~= "" and { "tia-lsp" } or { "lua", server_path }
|
||||||
|
|
||||||
vim.lsp.start({
|
vim.lsp.start({
|
||||||
name = "scl_lsp",
|
name = "tia_lsp",
|
||||||
cmd = { "lua", server_path },
|
cmd = cmd,
|
||||||
root_dir = root_dir,
|
root_dir = root_dir,
|
||||||
on_attach = function(client, b)
|
on_attach = function(client, b)
|
||||||
if opts.lsp and opts.lsp.on_attach then
|
if opts.lsp and opts.lsp.on_attach then
|
||||||
@@ -35,7 +41,7 @@ function M.setup(opts)
|
|||||||
|
|
||||||
if opts.lsp and opts.lsp.formatting then
|
if opts.lsp and opts.lsp.formatting then
|
||||||
vim.api.nvim_buf_create_user_command(b, "SCLFormat", function()
|
vim.api.nvim_buf_create_user_command(b, "SCLFormat", function()
|
||||||
vim.lsp.buf.format({ name = "scl_lsp" })
|
vim.lsp.buf.format({ name = "tia_lsp" })
|
||||||
end, {})
|
end, {})
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -52,7 +58,7 @@ function M.setup(opts)
|
|||||||
|
|
||||||
-- Create global format command (works on current buffer)
|
-- Create global format command (works on current buffer)
|
||||||
vim.api.nvim_create_user_command("SCLFormat", function()
|
vim.api.nvim_create_user_command("SCLFormat", function()
|
||||||
vim.lsp.buf.format({ name = "scl_lsp" })
|
vim.lsp.buf.format({ name = "tia_lsp" })
|
||||||
end, {})
|
end, {})
|
||||||
|
|
||||||
-- Start LSP for any existing scl buffers (handles lazy loading case)
|
-- Start LSP for any existing scl buffers (handles lazy loading case)
|
||||||
@@ -79,7 +85,11 @@ function M.setup_syntax(opts)
|
|||||||
if ok and parser_config then
|
if ok and parser_config then
|
||||||
parser_config.scl = {
|
parser_config.scl = {
|
||||||
install_info = {
|
install_info = {
|
||||||
url = "file://" .. vim.fn.expand("~/dev/scl_lsp"),
|
-- Override via setup({ ts_parser_url = "https://your-gitea/tia-lsp.git" }).
|
||||||
|
-- Default is the upstream TIA Portal LSP server repo (placeholder until
|
||||||
|
-- the split is complete; see README "Mason installation").
|
||||||
|
url = opts.ts_parser_url
|
||||||
|
or "file://" .. vim.fn.expand("~/dev/tia-lsp"),
|
||||||
files = { "src/parser.c" },
|
files = { "src/parser.c" },
|
||||||
generate_requires_npm = false,
|
generate_requires_npm = false,
|
||||||
requires_generate_from_grammar = false,
|
requires_generate_from_grammar = false,
|
||||||
@@ -92,7 +102,7 @@ function M.setup_syntax(opts)
|
|||||||
if opts.cmp then
|
if opts.cmp then
|
||||||
local function try_register()
|
local function try_register()
|
||||||
local cmp_ok, cmp = pcall(require, "blink.cmp")
|
local cmp_ok, cmp = pcall(require, "blink.cmp")
|
||||||
local blink_source_ok, blink_source = pcall(require, "scl.blink_cmp_source")
|
local blink_source_ok, blink_source = pcall(require, "tia.blink_cmp_source")
|
||||||
if cmp_ok and cmp and cmp.add_source_provider and blink_source_ok then
|
if cmp_ok and cmp and cmp.add_source_provider and blink_source_ok then
|
||||||
local source_config = {
|
local source_config = {
|
||||||
name = "scl",
|
name = "scl",
|
||||||
@@ -118,7 +128,7 @@ function M.setup_syntax(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if opts.workspace_types ~= false then
|
if opts.workspace_types ~= false then
|
||||||
local ok, workspace_types = pcall(require, "scl.workspace_types")
|
local ok, workspace_types = pcall(require, "tia.workspace_types")
|
||||||
if ok then
|
if ok then
|
||||||
workspace_types.setup({
|
workspace_types.setup({
|
||||||
project_root = opts.project_root,
|
project_root = opts.project_root,
|
||||||
@@ -130,14 +140,14 @@ function M.setup_syntax(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.setup_auto_prefix()
|
function M.setup_auto_prefix()
|
||||||
local ok, auto_prefix = pcall(require, "scl.auto_prefix")
|
local ok, auto_prefix = pcall(require, "tia.auto_prefix")
|
||||||
if ok then
|
if ok then
|
||||||
auto_prefix.setup()
|
auto_prefix.setup()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.setup_multiline_params()
|
function M.setup_multiline_params()
|
||||||
local ok, mp = pcall(require, "scl.multiline_params")
|
local ok, mp = pcall(require, "tia.multiline_params")
|
||||||
if not ok then
|
if not ok then
|
||||||
vim.notify("SCL multiline_params module not found", vim.log.levels.WARN)
|
vim.notify("SCL multiline_params module not found", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
@@ -163,14 +173,14 @@ function M.setup_multiline_params()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.prefix_current_word()
|
function M.prefix_current_word()
|
||||||
local ok, auto_prefix = pcall(require, "scl.auto_prefix")
|
local ok, auto_prefix = pcall(require, "tia.auto_prefix")
|
||||||
if ok then
|
if ok then
|
||||||
auto_prefix.prefix_word()
|
auto_prefix.prefix_word()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.show_variables()
|
function M.show_variables()
|
||||||
local ok, variables = pcall(require, "scl.variables")
|
local ok, variables = pcall(require, "tia.variables")
|
||||||
if not ok then
|
if not ok then
|
||||||
vim.notify("SCL variables module not found", vim.log.levels.WARN)
|
vim.notify("SCL variables module not found", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
@@ -191,9 +201,9 @@ function M.show_variables()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.show_workspace_types()
|
function M.show_workspace_types()
|
||||||
local ok_ws, ws = pcall(require, "scl.workspace_types")
|
local ok_ws, ws = pcall(require, "tia.workspace_types")
|
||||||
local ok_udt, udt = pcall(require, "scl.udt_parser")
|
local ok_udt, udt = pcall(require, "tia.udt_parser")
|
||||||
local ok_fb, fb = pcall(require, "scl.fb_parser")
|
local ok_fb, fb = pcall(require, "tia.fb_parser")
|
||||||
|
|
||||||
if not ok_ws or not ok_udt or not ok_fb then
|
if not ok_ws or not ok_udt or not ok_fb then
|
||||||
vim.notify("SCL workspace modules not found", vim.log.levels.WARN)
|
vim.notify("SCL workspace modules not found", vim.log.levels.WARN)
|
||||||
@@ -213,9 +223,9 @@ function M.show_workspace_types()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.rescan_workspace_types()
|
function M.rescan_workspace_types()
|
||||||
local ok_ws, ws = pcall(require, "scl.workspace_types")
|
local ok_ws, ws = pcall(require, "tia.workspace_types")
|
||||||
local ok_udt, udt = pcall(require, "scl.udt_parser")
|
local ok_udt, udt = pcall(require, "tia.udt_parser")
|
||||||
local ok_fb, fb = pcall(require, "scl.fb_parser")
|
local ok_fb, fb = pcall(require, "tia.fb_parser")
|
||||||
|
|
||||||
if not ok_ws or not ok_udt or not ok_fb then
|
if not ok_ws or not ok_udt or not ok_fb then
|
||||||
vim.notify("SCL workspace modules not found", vim.log.levels.WARN)
|
vim.notify("SCL workspace modules not found", vim.log.levels.WARN)
|
||||||
@@ -241,7 +251,7 @@ function M.create_commands()
|
|||||||
|
|
||||||
-- Attribute toggle commands
|
-- Attribute toggle commands
|
||||||
vim.api.nvim_create_user_command("SCLToggleAttrBlock", function()
|
vim.api.nvim_create_user_command("SCLToggleAttrBlock", function()
|
||||||
local ok, at = pcall(require, "scl.attr_toggle")
|
local ok, at = pcall(require, "tia.attr_toggle")
|
||||||
if ok then
|
if ok then
|
||||||
at.toggle_attr_block()
|
at.toggle_attr_block()
|
||||||
else
|
else
|
||||||
@@ -250,7 +260,7 @@ function M.create_commands()
|
|||||||
end, {})
|
end, {})
|
||||||
|
|
||||||
vim.api.nvim_create_user_command("SCLExpandAllAttrBlocks", function()
|
vim.api.nvim_create_user_command("SCLExpandAllAttrBlocks", function()
|
||||||
local ok, at = pcall(require, "scl.attr_toggle")
|
local ok, at = pcall(require, "tia.attr_toggle")
|
||||||
if ok then
|
if ok then
|
||||||
at.expand_all_attr_blocks()
|
at.expand_all_attr_blocks()
|
||||||
else
|
else
|
||||||
@@ -259,7 +269,7 @@ function M.create_commands()
|
|||||||
end, {})
|
end, {})
|
||||||
|
|
||||||
vim.api.nvim_create_user_command("SCLCollapseAllAttrBlocks", function()
|
vim.api.nvim_create_user_command("SCLCollapseAllAttrBlocks", function()
|
||||||
local ok, at = pcall(require, "scl.attr_toggle")
|
local ok, at = pcall(require, "tia.attr_toggle")
|
||||||
if ok then
|
if ok then
|
||||||
at.collapse_all_attr_blocks()
|
at.collapse_all_attr_blocks()
|
||||||
else
|
else
|
||||||
@@ -269,7 +279,7 @@ function M.create_commands()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.setup_attr_toggle()
|
function M.setup_attr_toggle()
|
||||||
local ok, at = pcall(require, "scl.attr_toggle")
|
local ok, at = pcall(require, "tia.attr_toggle")
|
||||||
if not ok then
|
if not ok then
|
||||||
vim.notify("SCL: attr_toggle module not found", vim.log.levels.WARN)
|
vim.notify("SCL: attr_toggle module not found", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
return {
|
return {
|
||||||
"lazar/scl_lsp",
|
"lazar/tia-lsp.nvim",
|
||||||
ft = "scl",
|
ft = "scl",
|
||||||
lazy = true,
|
lazy = true,
|
||||||
dependencies = {
|
dependencies = {
|
||||||
@@ -8,8 +8,9 @@ return {
|
|||||||
"saghen/blink.cmp",
|
"saghen/blink.cmp",
|
||||||
},
|
},
|
||||||
config = function(_, opts)
|
config = function(_, opts)
|
||||||
require("scl_lsp").setup({
|
require("tia_lsp").setup({
|
||||||
server_path = opts.server_path,
|
server_path = opts.server_path,
|
||||||
|
ts_parser_url = opts.ts_parser_url,
|
||||||
lsp = opts.lsp,
|
lsp = opts.lsp,
|
||||||
cmp = opts.cmp ~= false,
|
cmp = opts.cmp ~= false,
|
||||||
workspace_types = opts.workspace_types ~= false,
|
workspace_types = opts.workspace_types ~= false,
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
cd /home/lazar/dev/scl_lsp
|
|
||||||
lua src/main.lua
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@ local test = dofile("test/test_harness.lua")
|
|||||||
|
|
||||||
package.path = package.path .. ";lua/?.lua"
|
package.path = package.path .. ";lua/?.lua"
|
||||||
|
|
||||||
local db_parser = require("scl.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
|
|
||||||
local function test_db_parser_basic()
|
local function test_db_parser_basic()
|
||||||
local content = [[
|
local content = [[
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ local test = dofile("test/test_harness.lua")
|
|||||||
|
|
||||||
package.path = package.path .. ";lua/?.lua"
|
package.path = package.path .. ";lua/?.lua"
|
||||||
|
|
||||||
local fb_parser = require("scl.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
|
|
||||||
local function test_fb_parser_basic()
|
local function test_fb_parser_basic()
|
||||||
local content = [[
|
local content = [[
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ local test = dofile("test/test_harness.lua")
|
|||||||
|
|
||||||
package.path = package.path .. ";lua/?.lua;src/?.lua"
|
package.path = package.path .. ";lua/?.lua;src/?.lua"
|
||||||
|
|
||||||
local udt_parser = require("scl.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
local db_parser = require("scl.db_parser")
|
local db_parser = require("tia.db_parser")
|
||||||
local fb_parser = require("scl.fb_parser")
|
local fb_parser = require("tia.fb_parser")
|
||||||
|
|
||||||
local script_path = debug.getinfo(1, "S").source:gsub("^@", ""):match("(.*/)") or "."
|
local script_path = debug.getinfo(1, "S").source:gsub("^@", ""):match("(.*/)") or "."
|
||||||
script_path = script_path:gsub("^test/", ""):gsub("/tests$", "")
|
script_path = script_path:gsub("^test/", ""):gsub("/tests$", "")
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ local test = dofile("test/test_harness.lua")
|
|||||||
|
|
||||||
package.path = package.path .. ";lua/?.lua"
|
package.path = package.path .. ";lua/?.lua"
|
||||||
|
|
||||||
local udt_parser = require("scl.udt_parser")
|
local udt_parser = require("tia.udt_parser")
|
||||||
|
|
||||||
local function test_udt_parser_basic()
|
local function test_udt_parser_basic()
|
||||||
local content = [[
|
local content = [[
|
||||||
|
|||||||
Reference in New Issue
Block a user