Fix multiline params feature and auto-prefix

- Fix FB/function call parameter detection to prevent incorrect # prefixing
- Preserve # prefix when filling multiline params
- Add comma after first parameter and closing bracket on last param line
- Fix Tab navigation to position cursor after :=/=> operators
- Clean up dead code in multiline_params.lua
- Add setup functions for multiline params feature in both scl and scl_lsp
This commit is contained in:
2026-02-17 12:34:59 +01:00
parent 802fe93fb7
commit c1500f954b
4 changed files with 292 additions and 0 deletions
+40
View File
@@ -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
+25
View File
@@ -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", "<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()
+200
View File
@@ -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
+27
View File
@@ -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", "<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.prefix_current_word()
local ok, auto_prefix = pcall(require, "scl.auto_prefix")
if ok then