diff --git a/README.md b/README.md index 6e9922f..41b90fd 100644 --- a/README.md +++ b/README.md @@ -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 `` in insert mode | | **Workspace UDT Scanner** | Scans project for user-defined types (.udt files) | | **Workspace Global DB Scanner** | Scans project for global data blocks (.db files) | -| **blink.cmp Integration** | Smart completion source for blink.cmp | +| **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 `xa` | | **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. diff --git a/lua/tia/blink_cmp_source.lua b/lua/tia/blink_cmp_source.lua index 1ef739c..5097266 100644 --- a/lua/tia/blink_cmp_source.lua +++ b/lua/tia/blink_cmp_source.lua @@ -239,10 +239,16 @@ function M:get_completions(context, callback) local db_parser = require("tia.db_parser") 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 local db = db_parser.get_db(db_name) if db then - local path = active_portion:match('"[^"]+%"%.([%w_.]*)') + local path = active_portion:match('"[^"]+"%.([%w_.]*)$') -- Resolve nested path through DB members local current_type = nil @@ -269,15 +275,23 @@ function M:get_completions(context, callback) if found_member then resolved_path = part + local nested_type = nil 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") - current_members = udt_parser.get_udt_members(found_member.type) - current_type = found_member.type + current_members = udt_parser.get_udt_members(nested_type) + current_type = nested_type else -- This is a basic type, can't continue if i < #path_parts then - -- There are more parts but this isn't a UDT found = false break end @@ -319,14 +333,11 @@ function M:get_completions(context, callback) detail = m.raw_type, }) end - local wrapped_callback = vim.schedule_wrap(function(response) - callback({ - items = response.items, - is_incomplete_backward = false, - is_incomplete_forward = false, - }) - end) - wrapped_callback({ items = items }) + callback({ + items = items, + is_incomplete_backward = false, + is_incomplete_forward = false, + }) return end end @@ -959,6 +970,28 @@ function M:get_completions(context, callback) end 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 = {} }) return end diff --git a/lua/tia_lsp/init.lua b/lua/tia_lsp/init.lua index c8e4e9b..46de4c3 100644 --- a/lua/tia_lsp/init.lua +++ b/lua/tia_lsp/init.lua @@ -177,17 +177,18 @@ function M.setup_syntax(opts) if opts.cmp then local function try_register() local cmp_ok, cmp = pcall(require, "blink.cmp") - 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 - local source_config = { - name = "scl", - provider = blink_source, - score_offset = 100, - } - pcall(cmp.add_source_provider, "scl", source_config) - pcall(cmp.add_filetype_source, "scl", "scl") - - vim.notify("SCL: blink.cmp source registered", vim.log.levels.INFO) + local blink_source_ok = pcall(require, "tia.blink_cmp_source") + if cmp_ok and cmp and blink_source_ok then + if cmp.add_source_provider then + local blink_source = require("tia.blink_cmp_source") + local source_config = { + name = "scl", + provider = blink_source, + score_offset = 100, + } + pcall(cmp.add_source_provider, "scl", source_config) + pcall(cmp.add_filetype_source, "scl", "scl") + end return true end return false