-- SCL (Siemens Structured Control Language) support for Neovim -- Main setup module local M = {} function M.setup(opts) opts = opts or {} M.create_commands() -- Treesitter parser config local has_ts, ts = pcall(require, "nvim-treesitter.parsers") if has_ts and ts.get_parser_configs then local ok, parser_config = pcall(ts.get_parser_configs) if ok and parser_config then parser_config.scl = { install_info = { files = { "src/parser.c" }, generate_requires_npm = false, requires_generate_from_grammar = false, }, filetype = "scl", } end 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", "mp", function() mp.fill_multiline_params() end, { noremap = true, silent = true, desc = "SCL: Fill multiline parameters" }) vim.keymap.set("i", "", function() local ft = vim.bo.filetype if ft == "scl" or ft == "udt" then if mp.jump_to_next_param() then return "" end end return "" 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" }, "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", "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", "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