diff --git a/lua/scl/auto_prefix.lua b/lua/scl/auto_prefix.lua index ea4bebe..9df9b82 100644 --- a/lua/scl/auto_prefix.lua +++ b/lua/scl/auto_prefix.lua @@ -118,6 +118,36 @@ local function is_in_var_section() return in_var end +-- Check if we're inside an FB/Function call parameter list +local function is_in_fb_call_params() + local cursor_col = vim.api.nvim_win_get_cursor(0)[2] + local line = vim.api.nvim_get_current_line() + + -- If line ends with ");" or similar, we're at end of params + if line:match("%s*%);?%s*$") then + return false + end + + -- Count parens before cursor position + local open_count = 0 + local close_count = 0 + for i = 1, cursor_col do + local c = line:sub(i, i) + if c == "(" then + open_count = open_count + 1 + elseif c == ")" then + close_count = close_count + 1 + end + end + + -- If there are unclosed parens before cursor, we're inside params + if open_count > close_count then + return true + end + + return false +end + -- Check if we're after BEGIN keyword local function is_after_begin() local bufnr = vim.api.nvim_get_current_buf() @@ -301,6 +331,11 @@ local function auto_prefix_current_line() return end + -- Don't prefix if inside FB/Function call parameters + if is_in_fb_call_params() then + return + end + -- Only prefix after BEGIN if not is_after_begin() then return @@ -335,6 +370,11 @@ local function on_dot_typed() return end + -- Don't prefix if inside FB/Function call parameters + if is_in_fb_call_params() then + return + end + -- Only prefix after BEGIN if not is_after_begin() then return diff --git a/lua/scl/init.lua b/lua/scl/init.lua index 6b55d59..64c7571 100644 --- a/lua/scl/init.lua +++ b/lua/scl/init.lua @@ -85,6 +85,31 @@ function M.setup(opts) -- Auto-prefixing (enabled by default) M.setup_auto_prefix() + + -- Multiline parameters feature + M.setup_multiline_params() +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() diff --git a/lua/scl/multiline_params.lua b/lua/scl/multiline_params.lua new file mode 100644 index 0000000..7769b0a --- /dev/null +++ b/lua/scl/multiline_params.lua @@ -0,0 +1,200 @@ +local M = {} + +local function get_fb_interface(var_type) + local fb_parser = require("scl.fb_parser") + local clean_type = var_type:gsub('"', '') + + if fb_parser.get_cache_count() == 0 then + local ws = require("scl.workspace_types") + ws.scan_project_fbs(vim.api.nvim_get_current_buf()) + end + + return fb_parser.get_fb_interface(clean_type) +end + +local function is_fb_type(var_type) + local fb_parser = require("scl.fb_parser") + local clean_type = var_type:gsub('"', '') + + if fb_parser.get_cache_count() == 0 then + local ws = require("scl.workspace_types") + ws.scan_project_fbs(vim.api.nvim_get_current_buf()) + end + + return fb_parser.is_fb_type(clean_type) +end + +local function get_known_variables(bufnr) + local scl_vars = require("scl.variables") + local variables = scl_vars.extract_variables(bufnr) + local known = {} + for _, v in ipairs(variables) do + known[v.name] = true + end + return known +end + +local function get_variable_type(var_name, bufnr) + local scl_vars = require("scl.variables") + return scl_vars.get_variable_type(var_name, bufnr) +end + +function M.fill_multiline_params() + local bufnr = vim.api.nvim_get_current_buf() + local cursor = vim.api.nvim_win_get_cursor(0) + local row = cursor[1] + local col = cursor[2] + + local line = vim.api.nvim_get_current_line() + local line_before_cursor = line:sub(1, col) + + local known_vars = get_known_variables(bufnr) + + local fb_name = nil + local fb_prefix = "" + + local prefix, var_name = line_before_cursor:match("(#?)([%w_]+)%s*%(%s*$") + if var_name and known_vars[var_name] then + fb_prefix = prefix or "" + fb_name = var_name + end + + if not fb_name then + prefix, var_name = line_before_cursor:match("(#?)([%w_]+)%s*%([^)]-,%s*$") + if var_name and known_vars[var_name] then + fb_prefix = prefix or "" + fb_name = var_name + end + end + + if not fb_name then + vim.notify("No FB instance found before cursor", vim.log.levels.WARN) + return + end + + local var_type = get_variable_type(fb_name, bufnr) + if not var_type then + vim.notify("Cannot find type for: " .. fb_name, vim.log.levels.WARN) + return + end + + local clean_type = var_type:gsub('"', '') + if not is_fb_type(clean_type) then + vim.notify(clean_type .. " is not an FB/Function type", vim.log.levels.WARN) + return + end + + local fb_interface = get_fb_interface(clean_type) + if not fb_interface then + vim.notify("Cannot get interface for: " .. clean_type, vim.log.levels.WARN) + return + end + + local params = {} + for _, inp in ipairs(fb_interface.inputs or {}) do + table.insert(params, inp.name .. " := ") + end + for _, inout in ipairs(fb_interface.inouts or {}) do + table.insert(params, inout.name .. " := ") + end + for _, out in ipairs(fb_interface.outputs or {}) do + table.insert(params, out.name .. " => ") + end + + if #params == 0 then + vim.notify("No parameters found for: " .. clean_type, vim.log.levels.WARN) + return + end + + local indent = line:match("^%s*") or "" + + local continuation_indent = indent .. string.rep(" ", #fb_name + 2) + + local lines_to_insert = {} + + local first_line = indent .. fb_prefix .. fb_name .. "(" .. params[1] .. "," + table.insert(lines_to_insert, first_line) + + for i = 2, #params do + local p = params[i] + local closing = (i == #params) and ");" or "," + local line_content = continuation_indent .. p .. closing + table.insert(lines_to_insert, line_content) + end + + vim.api.nvim_buf_set_lines(bufnr, row - 1, row, false, lines_to_insert) + + local new_cursor_row = row + local new_cursor_col = #indent + #fb_prefix + #fb_name + 1 + #params[1] + + vim.api.nvim_win_set_cursor(0, { new_cursor_row, new_cursor_col }) +end + +function M.jump_to_next_param() + local cursor = vim.api.nvim_win_get_cursor(0) + local row = cursor[1] + local col = cursor[2] + + local bufnr = vim.api.nvim_get_current_buf() + local total_lines = vim.api.nvim_buf_line_count(bufnr) + + local function get_line_text(rownum) + return vim.api.nvim_buf_get_lines(bufnr, rownum - 1, rownum, false)[1] or "" + end + + local function find_next_param_on_line(start_row) + for i = start_row, total_lines do + local line = get_line_text(i) + local param_match = line:match("[%w_]+%s*:=") or line:match("[%w_]+%s*=>") + if param_match then + local op_end = line:find(":=") or line:find("=>") + vim.api.nvim_win_set_cursor(0, { i, op_end + 2 }) + return true + end + if line:match("%)") then + break + end + end + return false + end + + local current_line = get_line_text(row) + local text_after_cursor = current_line:sub(col + 1) + + local next_param = text_after_cursor:match("([%w_]+)%s*:=") or text_after_cursor:match("([%w_]+)%s*=>") + if next_param then + local op_end = text_after_cursor:find(":=") or text_after_cursor:find("=>") + local new_col = col + op_end + 2 + vim.api.nvim_win_set_cursor(0, { row, new_col }) + return true + end + + if not find_next_param_on_line(row + 1) then + local all_params = {} + local all_text = "" + for i = row, total_lines do + local l = get_line_text(i) + all_text = all_text .. l .. "\n" + if l:match("%)") then + break + end + end + for param in all_text:gmatch("([%w_]+)%s*:=") do + table.insert(all_params, param) + end + for param in all_text:gmatch("([%w_]+)%s*=>") do + table.insert(all_params, param) + end + + if #all_params > 0 then + vim.api.nvim_feedkeys("> ", "m", false) + return true + end + else + return true + end + + return false +end + +return M diff --git a/lua/scl_lsp/init.lua b/lua/scl_lsp/init.lua index be158bf..e823a6c 100644 --- a/lua/scl_lsp/init.lua +++ b/lua/scl_lsp/init.lua @@ -60,6 +60,7 @@ function M.setup(opts) } M.setup_syntax(opts) + M.setup_multiline_params() M.create_commands() if opts.auto_prefix ~= false then @@ -131,6 +132,32 @@ function M.setup_auto_prefix() end end +function M.setup_multiline_params() + local ok, mp = pcall(require, "scl.multiline_params") + if not ok then + vim.notify("SCL multiline_params module not found", vim.log.levels.WARN) + return + end + + 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.prefix_current_word() local ok, auto_prefix = pcall(require, "scl.auto_prefix") if ok then