refactor: rename scl_lsp → tia_lsp (project umbrella for future STL/LAD/FBD)
Rename the project from scl_lsp to tia_lsp to future-proof for additional
TIA Portal languages (STL/AWL, LAD, FBD). The 'scl' language name is kept
as-is for the SCL filetype and tree-sitter parser; future languages get
their own names (stl, lad, fbd).
Project-wide changes:
- lua/scl_lsp/ → lua/tia_lsp/ (plugin entry point)
- lua/scl/ → lua/tia/ (language modules, shared across TIA langs)
- require('scl.*') → require('tia.*')
- require('scl_lsp') → require('tia_lsp')
- LSP client name: 'scl_lsp' → 'tia_lsp'
- Diagnostic source: 'scl_lsp' → 'tia_lsp' (src/diagnostics.lua)
- package.json name: 'scl-language-server' → 'tia-lsp'
lua/tia_lsp/init.lua:
- Add vim.fn.exepath('tia-lsp') detection so the plugin prefers the
mason-installed executable and falls back to { 'lua', server_path }
for manual installs.
- Add opts.ts_parser_url to configure the tree-sitter parser source.
- Remove hardcoded ~/dev/scl_lsp paths in favor of ~/dev/tia-lsp and
the new ts_parser_url option.
Cleanup:
- Delete lua/tia/init.lua (dead code, unreferenced duplicate of tia_lsp).
- Delete scl_lsp.sh (replaced by the mason-installed bin/tia-lsp wrapper).
- Update README.md and AGENTS.md to reflect the two-repo split
(tia-lsp server + tia-lsp.nvim plugin) and document Mason installation.
Unchanged (intentional):
- grammar.js, src/grammar.json, src/parser.c: tree-sitter language name
remains 'scl' (it's the language, not the project).
- Filetype patterns ('scl', 'udt', 'db') in autocmds.
- :SCL* command names (per-filetype prefix; future :STL* etc.).
Tests pass with the same pre-existing failures as before the rename
(formatter VAR_INPUT test, udt_parser line 199 const-variable bug,
plc_json reference-project-missing). No new failures introduced.
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
-- Multiline parameter filling for SCL function block calls
|
||||
-- Automatically expands FB calls to multi-line format with all parameters
|
||||
|
||||
local M = {}
|
||||
|
||||
--- Get interface for built-in FB types (TON, TOF, etc.)
|
||||
--- @param var_type string The FB type name
|
||||
--- @return table|nil Interface with inputs, outputs, inouts
|
||||
local function get_builtin_interface(var_type)
|
||||
local builtin = require("tia.builtin_instructions")
|
||||
local clean_type = var_type:gsub('"', ''):gsub("#", ""):upper()
|
||||
local params_info = builtin.get_parameters(clean_type)
|
||||
if params_info then
|
||||
return {
|
||||
inputs = params_info.inputs or {},
|
||||
outputs = params_info.outputs or {},
|
||||
inouts = {},
|
||||
}
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get interface for custom FB types from workspace
|
||||
--- @param var_type string The FB type name
|
||||
--- @return table|nil Interface with inputs, outputs, inouts
|
||||
local function get_fb_interface(var_type)
|
||||
local fb_parser = require("tia.fb_parser")
|
||||
local clean_type = var_type:gsub('"', '')
|
||||
|
||||
if fb_parser.get_cache_count() == 0 then
|
||||
local ws = require("tia.workspace_types")
|
||||
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
||||
end
|
||||
|
||||
return fb_parser.get_fb_interface(clean_type)
|
||||
end
|
||||
|
||||
--- Check if a type is a known FB type
|
||||
--- @param var_type string The type name to check
|
||||
--- @return boolean True if type is an FB
|
||||
local function is_fb_type(var_type)
|
||||
local fb_parser = require("tia.fb_parser")
|
||||
local clean_type = var_type:gsub('"', '')
|
||||
|
||||
if fb_parser.get_cache_count() == 0 then
|
||||
local ws = require("tia.workspace_types")
|
||||
ws.scan_project_fbs(vim.api.nvim_get_current_buf())
|
||||
end
|
||||
|
||||
return fb_parser.is_fb_type(clean_type)
|
||||
end
|
||||
|
||||
--- Get list of known local variable names
|
||||
--- @param bufnr number Buffer number
|
||||
--- @return table Set of variable names
|
||||
local function get_known_variables(bufnr)
|
||||
local scl_vars = require("tia.variables")
|
||||
local variables = scl_vars.extract_variables(bufnr)
|
||||
local known = {}
|
||||
for _, v in ipairs(variables) do
|
||||
known[v.name] = true
|
||||
end
|
||||
return known
|
||||
end
|
||||
|
||||
--- Get the type of a variable by name
|
||||
--- @param var_name string Variable name
|
||||
--- @param bufnr number Buffer number
|
||||
--- @return string|nil Variable type, or nil if not found
|
||||
local function get_variable_type(var_name, bufnr)
|
||||
local scl_vars = require("tia.variables")
|
||||
return scl_vars.get_variable_type(var_name, bufnr)
|
||||
end
|
||||
|
||||
--- Fill multiline parameters for FB call at cursor
|
||||
--- Expands single-line call to multi-line with all parameters
|
||||
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 builtin = require("tia.builtin_instructions")
|
||||
|
||||
local fb_name = nil
|
||||
local fb_prefix = ""
|
||||
local is_builtin_function = false
|
||||
|
||||
local prefix, var_name = line_before_cursor:match("(#?)([%w_]+)%s*%(%s*$")
|
||||
if var_name then
|
||||
if known_vars[var_name] then
|
||||
fb_prefix = prefix or ""
|
||||
fb_name = var_name
|
||||
elseif builtin.is_known_function(var_name) then
|
||||
fb_prefix = prefix or ""
|
||||
fb_name = var_name
|
||||
is_builtin_function = true
|
||||
end
|
||||
end
|
||||
|
||||
if not fb_name then
|
||||
prefix, var_name = line_before_cursor:match("(#?)([%w_]+)%s*%([^)]-,%s*$")
|
||||
if var_name then
|
||||
if known_vars[var_name] then
|
||||
fb_prefix = prefix or ""
|
||||
fb_name = var_name
|
||||
elseif builtin.is_known_function(var_name) then
|
||||
fb_prefix = prefix or ""
|
||||
fb_name = var_name
|
||||
is_builtin_function = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not fb_name then
|
||||
vim.notify("No FB instance or known function found before cursor", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
local fb_interface = nil
|
||||
|
||||
if is_builtin_function then
|
||||
fb_interface = get_builtin_interface(fb_name)
|
||||
else
|
||||
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('"', '')
|
||||
|
||||
fb_interface = get_builtin_interface(clean_type)
|
||||
|
||||
if not fb_interface then
|
||||
if not is_fb_type(clean_type) then
|
||||
vim.notify(clean_type .. " is not an FB/Function type", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
fb_interface = get_fb_interface(clean_type)
|
||||
end
|
||||
end
|
||||
|
||||
if not fb_interface then
|
||||
vim.notify("Cannot get interface for: " .. (clean_type or fb_name), 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 or fb_name), 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
|
||||
|
||||
--- Check if cursor is inside an FB call parameter list
|
||||
--- @param bufnr number Buffer number
|
||||
--- @param cursor_row number Current row (1-based)
|
||||
--- @param cursor_col number Current column (1-based)
|
||||
--- @return boolean True if inside FB call params
|
||||
local function is_inside_fb_call(bufnr, cursor_row, cursor_col)
|
||||
local function get_line_text(rownum)
|
||||
return vim.api.nvim_buf_get_lines(bufnr, rownum - 1, rownum, false)[1] or ""
|
||||
end
|
||||
|
||||
local known_vars = get_known_variables(bufnr)
|
||||
|
||||
local paren_depth = 0
|
||||
local found_fb_call_start = false
|
||||
|
||||
for i = cursor_row, 1, -1 do
|
||||
local line = get_line_text(i)
|
||||
local line_to_cursor = (i == cursor_row) and line:sub(1, cursor_col) or line
|
||||
|
||||
for j = #line_to_cursor, 1, -1 do
|
||||
local char = line_to_cursor:sub(j, j)
|
||||
if char == ")" then
|
||||
paren_depth = paren_depth + 1
|
||||
elseif char == "(" then
|
||||
if paren_depth > 0 then
|
||||
paren_depth = paren_depth - 1
|
||||
else
|
||||
local before_paren = line_to_cursor:sub(1, j - 1)
|
||||
local var_match = before_paren:match("#?([%w_]+)%s*$")
|
||||
if var_match and known_vars[var_match] then
|
||||
return true
|
||||
end
|
||||
found_fb_call_start = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if found_fb_call_start then
|
||||
break
|
||||
end
|
||||
|
||||
if line:match("^%s*END_") or line:match("^%s*BEGIN") then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Jump to next parameter in FB call
|
||||
--- Used for Tab navigation within parameter lists
|
||||
--- @return boolean True if jumped to next param, false otherwise
|
||||
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()
|
||||
|
||||
if not is_inside_fb_call(bufnr, row, col) then
|
||||
return false
|
||||
end
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user