fix: DB completion path parsing, array-of-UDT resolution, blink source registration

- Fix path extraction pattern (missing closing quote, extra capture group)
- Add Array[..] of "UdtName" detection for nested member resolution
- Remove vim.schedule_wrap from DB member callback (causes blink.cmp timeout)
- Add built-in instruction parameter fallback (IEC_TIMER IN/PT/Q/ET)
- Ensure DB cache is populated on demand in completion source
- Make try_register() succeed without cmp.add_source_provider API
- docs: add blink.cmp source config steps to installation guide
This commit is contained in:
2026-07-22 20:26:16 +02:00
parent 3776ce33ff
commit 0a0b5c9bf1
3 changed files with 86 additions and 26 deletions
+28 -2
View File
@@ -11,7 +11,7 @@ A Neovim/LazyVim plugin that launches the [`tia-lsp`](https://gitea.l-tech.rs/la
| **Tab Navigation** | Jump between parameters with `<Tab>` in insert mode | | **Tab Navigation** | Jump between parameters with `<Tab>` in insert mode |
| **Workspace UDT Scanner** | Scans project for user-defined types (.udt files) | | **Workspace UDT Scanner** | Scans project for user-defined types (.udt files) |
| **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 — DB members, FB parameters, built-in types (IEC_TIMER IN/PT/Q/ET), nested UDT/array-of-UDT path resolution |
| **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 | | **Tree-sitter Highlighting** | Syntax highlighting via tree-sitter queries |
@@ -105,7 +105,33 @@ return {
} }
``` ```
**Step 5: Verify** **Step 5: Configure blink.cmp defaults**
The `scl` completion source must be listed in blink.cmp's `sources.default` with the corresponding provider definition. Create or update your blink.cmp config:
```lua
-- ~/.config/nvim/lua/plugins/blink.lua (or extend your existing blink.cmp spec)
return {
"saghen/blink.cmp",
optional = true,
opts = {
sources = {
default = { "lsp", "path", "snippets", "buffer", "scl" },
providers = {
scl = {
name = "scl",
module = "tia.blink_cmp_source",
score_offset = 100,
},
},
},
},
}
```
> **Why this is needed:** blink.cmp only queries sources listed in `sources.default`. Without `"scl"` there, the DB member and built-in type completions (e.g., `"MyDB".myField` or `#myTimer.IN`) will not appear.
**Step 6: Verify**
Open a `.scl` file and run `:LspInfo`. `tia_lsp` should be attached. Run `:checkhealth` for detailed diagnostics. Open a `.scl` file and run `:LspInfo`. `tia_lsp` should be attached. Run `:checkhealth` for detailed diagnostics.
+46 -13
View File
@@ -239,10 +239,16 @@ function M:get_completions(context, callback)
local db_parser = require("tia.db_parser") local db_parser = require("tia.db_parser")
local db_name = active_portion:match('"([%w_]+)"') local db_name = active_portion:match('"([%w_]+)"')
-- Ensure cache is populated
if db_name and db_parser.get_cache_count() == 0 then
local ws = require("tia.workspace_types")
ws.scan_project_dbs(bufnr)
end
if db_name then if db_name then
local db = db_parser.get_db(db_name) local db = db_parser.get_db(db_name)
if db then if db then
local path = active_portion:match('"[^"]+%"%.([%w_.]*)') local path = active_portion:match('"[^"]+"%.([%w_.]*)$')
-- Resolve nested path through DB members -- Resolve nested path through DB members
local current_type = nil local current_type = nil
@@ -269,15 +275,23 @@ function M:get_completions(context, callback)
if found_member then if found_member then
resolved_path = part resolved_path = part
local nested_type = nil
if found_member.is_udt then if found_member.is_udt then
-- This member is a UDT type, continue resolving nested_type = found_member.type
else
-- Check for array of UDT: Array[..] of "TypeName"
local array_udt = found_member.raw_type:match('Array%b[]%s*of%s*"([^"]+)"')
if array_udt then
nested_type = array_udt
end
end
if nested_type then
local udt_parser = require("tia.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(nested_type)
current_type = found_member.type current_type = nested_type
else else
-- This is a basic type, can't continue -- This is a basic type, can't continue
if i < #path_parts then if i < #path_parts then
-- There are more parts but this isn't a UDT
found = false found = false
break break
end end
@@ -319,14 +333,11 @@ function M:get_completions(context, callback)
detail = m.raw_type, detail = m.raw_type,
}) })
end end
local wrapped_callback = vim.schedule_wrap(function(response) callback({
callback({ items = items,
items = response.items, is_incomplete_backward = false,
is_incomplete_backward = false, is_incomplete_forward = false,
is_incomplete_forward = false, })
})
end)
wrapped_callback({ items = items })
return return
end end
end end
@@ -959,6 +970,28 @@ function M:get_completions(context, callback)
end end
if not is_udt and not is_fb then if not is_udt and not is_fb then
-- Check built-in instructions (IEC_TIMER, TON, etc.)
local builtin = require("tia.builtin_instructions")
local builtin_params = builtin.get_parameters(var_type)
if builtin_params then
local items = {}
for _, inp in ipairs(builtin_params.inputs or {}) do
table.insert(items, {
label = inp.name,
kind = vim.lsp.protocol.CompletionItemKind.Field,
detail = (inp.type or "ANY") .. " (Input)",
})
end
for _, out in ipairs(builtin_params.outputs or {}) do
table.insert(items, {
label = out.name,
kind = vim.lsp.protocol.CompletionItemKind.Field,
detail = (out.type or "ANY") .. " (Output)",
})
end
wrapped_callback({ items = items })
return
end
wrapped_callback({ items = {} }) wrapped_callback({ items = {} })
return return
end end
+12 -11
View File
@@ -177,17 +177,18 @@ 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, "tia.blink_cmp_source") local blink_source_ok = 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 blink_source_ok then
local source_config = { if cmp.add_source_provider then
name = "scl", local blink_source = require("tia.blink_cmp_source")
provider = blink_source, local source_config = {
score_offset = 100, name = "scl",
} provider = blink_source,
pcall(cmp.add_source_provider, "scl", source_config) score_offset = 100,
pcall(cmp.add_filetype_source, "scl", "scl") }
pcall(cmp.add_source_provider, "scl", source_config)
vim.notify("SCL: blink.cmp source registered", vim.log.levels.INFO) pcall(cmp.add_filetype_source, "scl", "scl")
end
return true return true
end end
return false return false