Files
lazar 8c6a050efb 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.
2026-07-18 11:30:55 +02:00

253 lines
7.0 KiB
Lua

local test = dofile("test/test_harness.lua")
package.path = package.path .. ";lua/?.lua"
local udt_parser = require("tia.udt_parser")
local function test_udt_parser_basic()
local content = [[
TYPE "test_udt"
TITLE = Test UDT
VERSION : 0.1
//Test type definition
STRUCT
member1 : Bool;
member2 : Int;
member3 : "other_udt";
END_STRUCT;
END_TYPE
]]
local udt, err = udt_parser.parse_udt_content(content, "test.udt")
if udt then
udt_parser.register_inline_udt(udt)
end
test.assert_not_nil(udt, "parse_udt_content should return udt")
test.assert_equals(udt.name, "test_udt", "type name should be extracted")
test.assert_equals(#udt.members, 3, "should have 3 members")
test.assert_equals(udt.members[1].name, "member1", "first member name")
test.assert_equals(udt.members[1].type, "Bool", "first member type")
test.assert_equals(udt.members[3].is_udt, true, "third member is UDT type")
end
local function test_udt_parser_attributes()
local content = [[
TYPE "attr_test"
TITLE = Attr Test
VERSION : 0.1
STRUCT
attrMember{S7_SetPoint := 'True'}: Bool;
normalMember : Int;
END_STRUCT;
END_TYPE
]]
local udt = udt_parser.parse_udt_content(content, "attr.udt")
test.assert_not_nil(udt, "parse_udt_content should return udt")
test.assert_equals(udt.members[1].attributes.S7_SetPoint, "True", "attribute should be parsed")
test.assert(udt.members[2].attributes == nil or next(udt.members[2].attributes) == nil, "normal member has no attributes")
end
local function test_udt_parser_comments()
local content = [[
TYPE "comment_test"
TITLE = Comment Test
VERSION : 0.1
//This is a type comment
STRUCT
member1 : Bool; //first member
member2 : Int; //second member
END_STRUCT;
END_TYPE
]]
local udt = udt_parser.parse_udt_content(content, "comment.udt")
test.assert_not_nil(udt, "parse_udt_content should return udt")
test.assert_equals(udt.comment, "This is a type comment", "type comment should be extracted")
test.assert_equals(udt.members[1].comment, "first member", "member comment should be extracted")
end
local function test_udt_parser_array()
local content = [[
TYPE "array_test"
TITLE = Array Test
VERSION : 0.1
STRUCT
arr : Array[0..9] of Int;
arr2 : Array[0..5, 0..3] of Bool;
END_STRUCT;
END_TYPE
]]
local udt = udt_parser.parse_udt_content(content, "array.udt")
test.assert_not_nil(udt, "parse_udt_content should return udt")
test.assert(udt.members[1].type:match("Array"), "array type should be preserved")
end
local function test_udt_cache_operations()
udt_parser.clear_cache()
test.assert_equals(udt_parser.get_cache_count(), 0, "cache should be empty after clear")
local content = [[
TYPE "cached_udt"
TITLE = Cached UDT
VERSION : 0.1
STRUCT
m1 : Bool;
END_STRUCT;
END_TYPE
]]
local udt = udt_parser.parse_udt_content(content, "cached.udt")
if udt then
udt_parser.register_inline_udt(udt)
end
local cached = udt_parser.get_udt("cached_udt")
test.assert_not_nil(cached, "UDT should be cached")
test.assert_equals(udt_parser.get_cache_count(), 1, "cache should have 1 entry")
local names = udt_parser.get_all_udt_names()
test.assert_equals(#names, 1, "should have 1 UDT name")
test.assert_equals(names[1], "cached_udt", "UDT name should match")
end
local function test_udt_is_type()
udt_parser.clear_cache()
local content = [[
TYPE "known_type"
TITLE = Known
VERSION : 0.1
STRUCT
m1 : Bool;
END_STRUCT;
END_TYPE
]]
local udt = udt_parser.parse_udt_content(content, "known.udt")
if udt then
udt_parser.register_inline_udt(udt)
end
test.assert(udt_parser.is_udt_type("known_type"), "known_type should be recognized")
test.assert(udt_parser.is_udt_type('"known_type"'), "quoted known_type should be recognized")
test.assert(not udt_parser.is_udt_type("unknown_type"), "unknown_type should not be recognized")
end
local function test_udt_members()
udt_parser.clear_cache()
local content1 = [[
TYPE "inner_type"
TITLE = Inner
VERSION : 0.1
STRUCT
x : Int;
END_STRUCT;
END_TYPE
]]
local content2 = [[
TYPE "outer_type"
TITLE = Outer
VERSION : 0.1
STRUCT
inner : "inner_type";
simple : Bool;
END_STRUCT;
END_TYPE
]]
local udt1 = udt_parser.parse_udt_content(content1, "inner.udt")
local udt2 = udt_parser.parse_udt_content(content2, "outer.udt")
if udt1 then udt_parser.register_inline_udt(udt1) end
if udt2 then udt_parser.register_inline_udt(udt2) end
local members = udt_parser.get_udt_members("outer_type")
test.assert_not_nil(members, "members should not be nil")
if members and #members > 0 then
test.assert_equals(members[1].name, "inner", "first member name")
test.assert_equals(members[1].is_udt, true, "first member is UDT type")
end
end
local function test_udt_reference_files()
udt_parser.clear_cache()
local ref_path = test.REF_PROJECT .. "/PLC data types/99_Library/Manz Siemens Standard/01_Machine/LMS_typePartCounter.udt"
local file = io.open(ref_path, "r")
if not file then
print("Warning: Reference UDT file not found: " .. ref_path)
return
end
local content = file:read("*all")
file:close()
local udt = udt_parser.parse_udt_content(content, ref_path)
if udt then
udt_parser.register_inline_udt(udt)
end
test.assert_not_nil(udt, "should parse reference UDT file")
test.assert_equals(udt.name, "LMS_typePartCounter", "UDT name should match")
test.assert_gt(#udt.members, 0, "should have members")
local cached = udt_parser.get_udt("LMS_typePartCounter")
test.assert_not_nil(cached, "UDT should be cached")
end
local function test_udt_multiple_reference_files()
udt_parser.clear_cache()
local base_path = test.REF_PROJECT .. "/PLC data types/99_Library/Manz Siemens Standard/"
local files = {
base_path .. "01_Machine/LMS_typePartCounter.udt",
base_path .. "01_Machine/LMS_typePartCounterCommands.udt",
base_path .. "01_Machine/LMS_typePartCounterStatus.udt",
}
local parsed_count = 0
for _, filepath in ipairs(files) do
local file = io.open(filepath, "r")
if file then
local content = file:read("*all")
file:close()
local udt = udt_parser.parse_udt_content(content, filepath)
if udt then
udt_parser.register_inline_udt(udt)
parsed_count = parsed_count + 1
end
end
end
test.assert_gt(parsed_count, 0, "should parse at least one reference UDT")
test.assert_equals(udt_parser.get_cache_count(), parsed_count, "cache should have correct count")
end
local function run_udt_tests()
test.run_suite("UDT Parser Tests", function()
print("\n--- Basic Tests ---")
test_udt_parser_basic()
test_udt_parser_attributes()
test_udt_parser_comments()
test_udt_parser_array()
print("\n--- Cache Tests ---")
test_udt_cache_operations()
test_udt_is_type()
test_udt_members()
print("\n--- Reference Project Tests ---")
test_udt_reference_files()
test_udt_multiple_reference_files()
end)
end
if vim then
vim.api.nvim_create_user_command("TestUdt", function()
run_udt_tests()
end, {})
end
run_udt_tests()