feat: improve tree-sitter syntax highlighting with named nodes

- Add aliases for keywords (VAR_INPUT, BEGIN, IF, etc.) as named nodes
- Add aliases for built-in types (INT, BOOL, STRING, etc.) as type_builtin
- Add aliases for constants (TRUE, FALSE)
- Add aliases for operators (AND, OR, NOT, MOD)
- Add prefix node for # variable prefix highlighting
- Highlight FB instance names in calls
- Highlight FB parameter names as properties
- Update README with detailed syntax highlighting features
- Update .gitignore for *.wasm files
This commit is contained in:
2026-02-20 23:29:26 +01:00
parent dfa4ef3381
commit 553192e7a1
10 changed files with 60598 additions and 55374 deletions
+61
View File
@@ -226,6 +226,67 @@ M.DATA_TYPES = {
DTL = true,
}
M.PARAMETERS = {
TON = { inputs = { { name = "IN", type = "BOOL" }, { name = "PT", type = "TIME" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "ET", type = "TIME" } } },
TOF = { inputs = { { name = "IN", type = "BOOL" }, { name = "PT", type = "TIME" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "ET", type = "TIME" } } },
TP = { inputs = { { name = "IN", type = "BOOL" }, { name = "PT", type = "TIME" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "ET", type = "TIME" } } },
TONR = { inputs = { { name = "IN", type = "BOOL" }, { name = "PT", type = "TIME" }, { name = "R", type = "BOOL" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "ET", type = "TIME" } } },
CTU = { inputs = { { name = "CU", type = "BOOL" }, { name = "R", type = "BOOL" }, { name = "PV", type = "INT" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "CV", type = "INT" } } },
CTD = { inputs = { { name = "CD", type = "BOOL" }, { name = "LD", type = "BOOL" }, { name = "PV", type = "INT" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "CV", type = "INT" } } },
CTUD = { inputs = { { name = "CU", type = "BOOL" }, { name = "CD", type = "BOOL" }, { name = "R", type = "BOOL" }, { name = "LD", type = "BOOL" }, { name = "PV", type = "INT" } }, outputs = { { name = "QU", type = "BOOL" }, { name = "QD", type = "BOOL" }, { name = "CV", type = "INT" } } },
R_TRIG = { inputs = { { name = "CLK", type = "BOOL" } }, outputs = { { name = "Q", type = "BOOL" } } },
F_TRIG = { inputs = { { name = "CLK", type = "BOOL" } }, outputs = { { name = "Q", type = "BOOL" } } },
SR = { inputs = { { name = "S", type = "BOOL" }, { name = "R1", type = "BOOL" } }, outputs = { { name = "Q1", type = "BOOL" } } },
RS = { inputs = { { name = "S1", type = "BOOL" }, { name = "R", type = "BOOL" } }, outputs = { { name = "Q1", type = "BOOL" } } },
IEC_TIMER = { inputs = { { name = "IN", type = "BOOL" }, { name = "PT", type = "TIME" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "ET", type = "TIME" } } },
IEC_TON = { inputs = { { name = "IN", type = "BOOL" }, { name = "PT", type = "TIME" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "ET", type = "TIME" } } },
IEC_TOF = { inputs = { { name = "IN", type = "BOOL" }, { name = "PT", type = "TIME" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "ET", type = "TIME" } } },
IEC_TP = { inputs = { { name = "IN", type = "BOOL" }, { name = "PT", type = "TIME" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "ET", type = "TIME" } } },
IEC_COUNTER = { inputs = { { name = "CU", type = "BOOL" }, { name = "CD", type = "BOOL" }, { name = "R", type = "BOOL" }, { name = "LD", type = "BOOL" }, { name = "PV", type = "INT" } }, outputs = { { name = "QU", type = "BOOL" }, { name = "QD", type = "BOOL" }, { name = "CV", type = "INT" } } },
IEC_CTU = { inputs = { { name = "CU", type = "BOOL" }, { name = "R", type = "BOOL" }, { name = "PV", type = "INT" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "CV", type = "INT" } } },
IEC_CTD = { inputs = { { name = "CD", type = "BOOL" }, { name = "LD", type = "BOOL" }, { name = "PV", type = "INT" } }, outputs = { { name = "Q", type = "BOOL" }, { name = "CV", type = "INT" } } },
IEC_CTUD = { inputs = { { name = "CU", type = "BOOL" }, { name = "CD", type = "BOOL" }, { name = "R", type = "BOOL" }, { name = "LD", type = "BOOL" }, { name = "PV", type = "INT" } }, outputs = { { name = "QU", type = "BOOL" }, { name = "QD", type = "BOOL" }, { name = "CV", type = "INT" } } },
ADD = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
SUB = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
MUL = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
DIV = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
MIN = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
MAX = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
LIMIT = { inputs = { { name = "MN" }, { name = "IN" }, { name = "MX" } }, outputs = { { name = "OUT" } } },
MOVE = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
CONVERT = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
ROUND = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
TRUNC = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
CEIL = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
FLOOR = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
SQR = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
SQRT = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
SIN = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
COS = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
TAN = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
ASIN = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
ACOS = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
ATAN = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
LEN = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
CONCAT = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
LEFT = { inputs = { { name = "IN" }, { name = "L" } }, outputs = { { name = "OUT" } } },
RIGHT = { inputs = { { name = "IN" }, { name = "L" } }, outputs = { { name = "OUT" } } },
MID = { inputs = { { name = "IN" }, { name = "L" }, { name = "P" } }, outputs = { { name = "OUT" } } },
AND = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
OR = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
XOR = { inputs = { { name = "IN1" }, { name = "IN2" } }, outputs = { { name = "OUT" } } },
SHL = { inputs = { { name = "IN" }, { name = "N" } }, outputs = { { name = "OUT" } } },
SHR = { inputs = { { name = "IN" }, { name = "N" } }, outputs = { { name = "OUT" } } },
ROL = { inputs = { { name = "IN" }, { name = "N" } }, outputs = { { name = "OUT" } } },
ROR = { inputs = { { name = "IN" }, { name = "N" } }, outputs = { { name = "OUT" } } },
BCD_I = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
I_BCD = { inputs = { { name = "IN" } }, outputs = { { name = "OUT" } } },
}
function M.get_parameters(name)
return M.PARAMETERS[name:upper()]
end
function M.is_known_function(name)
return M.FUNCTIONS[name:upper()] or false
end
+1115 -286
View File
File diff suppressed because it is too large Load Diff
+175 -3
View File
@@ -335,6 +335,30 @@ function handlers.textDocument_hover(params)
var_info.data_type or "unknown",
var_info.line + 1
)
if var_info.comment and var_info.comment ~= "" then
detail = detail .. "\n\n" .. var_info.comment
end
if var_info.data_type then
local builtin_params = builtin.get_parameters(var_info.data_type)
if builtin_params then
detail = detail .. "\n\n**Members:**\n"
if builtin_params.inputs and #builtin_params.inputs > 0 then
detail = detail .. "*Inputs:*\n"
for _, inp in ipairs(builtin_params.inputs) do
detail = detail .. string.format(" - `%s`: %s\n", inp.name, inp.type or "ANY")
end
end
if builtin_params.outputs and #builtin_params.outputs > 0 then
detail = detail .. "*Outputs:*\n"
for _, out in ipairs(builtin_params.outputs) do
detail = detail .. string.format(" - `%s`: %s\n", out.name, out.type or "ANY")
end
end
end
end
return {
contents = { kind = "markdown", value = detail },
range = {
@@ -348,10 +372,17 @@ function handlers.textDocument_hover(params)
for type_name, type_info in pairs(doc.types) do
if type_name == word then
local detail = string.format("**Type**: `%s`\nKind: %s", type_name, type_info.kind)
if type_info.comment and type_info.comment ~= "" then
detail = detail .. "\n\n" .. type_info.comment
end
if type_info.kind == "struct" and type_info.fields then
detail = detail .. "\n\n**Fields:**\n"
for _, field in ipairs(type_info.fields) do
detail = detail .. string.format("- `%s`: %s\n", field.name, field.type)
if field.comment and field.comment ~= "" then
detail = detail .. string.format("- `%s`: %s // %s\n", field.name, field.type, field.comment)
else
detail = detail .. string.format("- `%s`: %s\n", field.name, field.type)
end
end
end
return {
@@ -365,6 +396,124 @@ function handlers.textDocument_hover(params)
end
end
if builtin.is_known_function_block(word) or builtin.is_known_function(word) then
local params_info = builtin.get_parameters(word)
local kind = builtin.is_known_function_block(word) and "Function Block" or "Function"
local detail = string.format("**%s**: `%s`\n", kind, word)
if params_info then
detail = detail .. "\n**Parameters:**\n"
if params_info.inputs and #params_info.inputs > 0 then
detail = detail .. "*Inputs:*\n"
for _, inp in ipairs(params_info.inputs) do
detail = detail .. string.format(" - `%s`: %s\n", inp.name, inp.type or "ANY")
end
end
if params_info.outputs and #params_info.outputs > 0 then
detail = detail .. "*Outputs:*\n"
for _, out in ipairs(params_info.outputs) do
detail = detail .. string.format(" - `%s`: %s\n", out.name, out.type or "ANY")
end
end
else
detail = detail .. "\n_(No parameter info available)_"
end
return {
contents = { kind = "markdown", value = detail },
range = {
start = { line = params.position.line, character = word_start - 1 },
["end"] = { line = params.position.line, character = word_end - 1 },
},
}
end
local fb_param_info = nil
local function find_fb_call_start(lines, current_line_idx)
for i = current_line_idx, 1, -1 do
local l = lines[i]
local fb_name = l:match("#([%w_]+)%s*%(") or l:match("([%w_]+)%s*%(")
if fb_name and doc.variables and doc.variables[fb_name] then
return fb_name, i
end
if l:match("%)%s*;%s*$") then
break
end
end
return nil, nil
end
local fb_name_on_line, _ = find_fb_call_start(lines, line_idx)
if fb_name_on_line then
local fb_var = doc.variables[fb_name_on_line]
if fb_var then
local fb_type = fb_var.data_type
if fb_type then
local clean_type = fb_type:gsub('"', ""):gsub("#", "")
local params_info = builtin.get_parameters(clean_type)
if not params_info then
local workspace_types = plc_json.get_types(doc.root_dir)
if workspace_types and workspace_types[clean_type] then
local ws_type = workspace_types[clean_type]
if ws_type.kind == "function_block" or ws_type.kind == "function" then
local fields = {}
for _, f in ipairs(ws_type.inputs or {}) do
table.insert(fields, { name = f.name, type = f.type, comment = f.comment })
end
params_info = { inputs = fields, outputs = ws_type.outputs or {} }
elseif ws_type.fields then
local fields = {}
for _, f in ipairs(ws_type.fields) do
table.insert(fields, { name = f.name, type = f.type, comment = f.comment })
end
params_info = { inputs = fields, outputs = {} }
end
end
end
if not params_info and doc.types and doc.types[clean_type] and doc.types[clean_type].fields then
local fields = {}
for _, f in ipairs(doc.types[clean_type].fields) do
table.insert(fields, { name = f.name, type = f.type, comment = f.comment })
end
params_info = { inputs = fields, outputs = {} }
end
if params_info then
for _, inp in ipairs(params_info.inputs or {}) do
if inp.name:upper() == word:upper() then
fb_param_info = { name = inp.name, type = inp.type, direction = "input", comment = inp.comment }
break
end
end
if not fb_param_info then
for _, out in ipairs(params_info.outputs or {}) do
if out.name:upper() == word:upper() then
fb_param_info = { name = out.name, type = out.type, direction = "output", comment = out.comment }
break
end
end
end
end
end
end
end
if fb_param_info then
local detail = string.format("**Parameter**: `%s`\nType: `%s`\nDirection: %s",
fb_param_info.name, fb_param_info.type or "ANY", fb_param_info.direction)
if fb_param_info.comment and fb_param_info.comment ~= "" then
detail = detail .. "\n\n" .. fb_param_info.comment
end
return {
contents = { kind = "markdown", value = detail },
range = {
start = { line = params.position.line, character = word_start - 1 },
["end"] = { line = params.position.line, character = word_end - 1 },
},
}
end
return nil
end
@@ -432,9 +581,10 @@ function handlers.textDocument_completion(params)
local var_info = doc.variables and doc.variables[var_name]
local data_type = var_info and var_info.data_type
if data_type and doc.types then
if data_type then
local struct_name = data_type
if doc.types[struct_name] and doc.types[struct_name].fields then
if doc.types and doc.types[struct_name] and doc.types[struct_name].fields then
for _, field in ipairs(doc.types[struct_name].fields) do
table.insert(items, {
label = field.name,
@@ -444,6 +594,28 @@ function handlers.textDocument_completion(params)
insertTextFormat = 1,
})
end
else
local builtin_params = builtin.get_parameters(struct_name)
if builtin_params then
for _, inp in ipairs(builtin_params.inputs or {}) do
table.insert(items, {
label = inp.name,
kind = 13,
detail = struct_name .. "." .. inp.name .. ": " .. (inp.type or "ANY"),
insertText = inp.name,
insertTextFormat = 1,
})
end
for _, out in ipairs(builtin_params.outputs or {}) do
table.insert(items, {
label = out.name,
kind = 13,
detail = struct_name .. "." .. out.name .. ": " .. (out.type or "ANY"),
insertText = out.name,
insertTextFormat = 1,
})
end
end
end
end
end
+216 -431
View File
@@ -84,8 +84,12 @@
"fields": {},
"children": {
"multiple": false,
"required": false,
"required": true,
"types": [
{
"type": "constant",
"named": true
},
{
"type": "identifier",
"named": true
@@ -108,6 +112,10 @@
{
"type": "expression",
"named": true
},
{
"type": "operator",
"named": true
}
]
}
@@ -162,6 +170,25 @@
{
"type": "expression",
"named": true
},
{
"type": "keyword",
"named": true
}
]
}
},
{
"type": "continue_statement",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "keyword",
"named": true
}
]
}
@@ -172,8 +199,12 @@
"fields": {},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "keyword",
"named": true
},
{
"type": "statement",
"named": true
@@ -193,6 +224,10 @@
"type": "expression",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "statement",
"named": true
@@ -205,6 +240,21 @@
"named": true,
"fields": {}
},
{
"type": "exit_statement",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "keyword",
"named": true
}
]
}
},
{
"type": "expression",
"named": true,
@@ -264,6 +314,77 @@
]
}
},
{
"type": "fb_call",
"named": true,
"fields": {
"instance": {
"multiple": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
},
{
"type": "prefixed_identifier",
"named": true
}
]
}
},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "fb_parameter_list",
"named": true
}
]
}
},
{
"type": "fb_parameter",
"named": true,
"fields": {
"name": {
"multiple": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
}
]
}
},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "expression",
"named": true
}
]
}
},
{
"type": "fb_parameter_list",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "fb_parameter",
"named": true
}
]
}
},
{
"type": "field_access",
"named": true,
@@ -303,6 +424,10 @@
"type": "identifier",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "statement",
"named": true
@@ -327,7 +452,7 @@
},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "attribute_list",
@@ -341,6 +466,10 @@
"type": "identifier",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "number",
"named": true
@@ -397,7 +526,7 @@
},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "attribute_list",
@@ -407,6 +536,10 @@
"type": "identifier",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "number",
"named": true
@@ -484,6 +617,10 @@
"type": "expression",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "statement",
"named": true
@@ -539,7 +676,7 @@
},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "attribute_list",
@@ -549,6 +686,10 @@
"type": "identifier",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "number",
"named": true
@@ -607,12 +748,16 @@
"named": true,
"fields": {},
"children": {
"multiple": false,
"multiple": true,
"required": true,
"types": [
{
"type": "identifier",
"named": true
},
{
"type": "prefix",
"named": true
}
]
}
@@ -659,6 +804,10 @@
"multiple": true,
"required": true,
"types": [
{
"type": "keyword",
"named": true
},
{
"type": "region_name",
"named": true
@@ -682,6 +831,10 @@
"type": "expression",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "statement",
"named": true
@@ -745,6 +898,10 @@
"type": "exit_statement",
"named": true
},
{
"type": "fb_call",
"named": true
},
{
"type": "for_statement",
"named": true
@@ -803,12 +960,16 @@
"fields": {},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "number",
"named": true
@@ -820,6 +981,10 @@
{
"type": "type",
"named": true
},
{
"type": "type_builtin",
"named": true
}
]
}
@@ -829,12 +994,16 @@
"named": true,
"fields": {},
"children": {
"multiple": false,
"multiple": true,
"required": true,
"types": [
{
"type": "expression",
"named": true
},
{
"type": "operator",
"named": true
}
]
}
@@ -845,12 +1014,16 @@
"fields": {},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "block_comment",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "var_item",
"named": true
@@ -864,12 +1037,16 @@
"fields": {},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "block_comment",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "var_item",
"named": true
@@ -923,12 +1100,16 @@
"fields": {},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "block_comment",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "var_item",
"named": true
@@ -942,12 +1123,16 @@
"fields": {},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "block_comment",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "var_item",
"named": true
@@ -961,12 +1146,16 @@
"fields": {},
"children": {
"multiple": true,
"required": false,
"required": true,
"types": [
{
"type": "block_comment",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "var_item",
"named": true
@@ -986,6 +1175,10 @@
"type": "expression",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "statement",
"named": true
@@ -1001,10 +1194,6 @@
"type": "\"\"",
"named": false
},
{
"type": "#",
"named": false
},
{
"type": "'",
"named": false
@@ -1089,346 +1278,6 @@
"type": ">=",
"named": false
},
{
"type": "AND",
"named": false
},
{
"type": "ARRAY",
"named": false
},
{
"type": "BEGIN",
"named": false
},
{
"type": "BOOL",
"named": false
},
{
"type": "BY",
"named": false
},
{
"type": "BYTE",
"named": false
},
{
"type": "Bool",
"named": false
},
{
"type": "Byte",
"named": false
},
{
"type": "CASE",
"named": false
},
{
"type": "CHAR",
"named": false
},
{
"type": "CONSTANT",
"named": false
},
{
"type": "Char",
"named": false
},
{
"type": "DINT",
"named": false
},
{
"type": "DInt",
"named": false
},
{
"type": "DO",
"named": false
},
{
"type": "DT",
"named": false
},
{
"type": "DWORD",
"named": false
},
{
"type": "DWord",
"named": false
},
{
"type": "Dt",
"named": false
},
{
"type": "ELSE",
"named": false
},
{
"type": "ELSIF",
"named": false
},
{
"type": "END_CASE",
"named": false
},
{
"type": "END_FOR",
"named": false
},
{
"type": "END_FUNCTION",
"named": false
},
{
"type": "END_FUNCTION_BLOCK",
"named": false
},
{
"type": "END_IF",
"named": false
},
{
"type": "END_ORGANIZATION_BLOCK",
"named": false
},
{
"type": "END_REGION",
"named": false
},
{
"type": "END_REPEAT",
"named": false
},
{
"type": "END_VAR",
"named": false
},
{
"type": "END_WHILE",
"named": false
},
{
"type": "FALSE",
"named": false
},
{
"type": "FOR",
"named": false
},
{
"type": "FUNCTION",
"named": false
},
{
"type": "FUNCTION_BLOCK",
"named": false
},
{
"type": "IF",
"named": false
},
{
"type": "INT",
"named": false
},
{
"type": "Int",
"named": false
},
{
"type": "LREAL",
"named": false
},
{
"type": "LReal",
"named": false
},
{
"type": "MOD",
"named": false
},
{
"type": "NON_RETAIN",
"named": false
},
{
"type": "NOT",
"named": false
},
{
"type": "OF",
"named": false
},
{
"type": "OR",
"named": false
},
{
"type": "ORGANIZATION_BLOCK",
"named": false
},
{
"type": "REAL",
"named": false
},
{
"type": "REGION",
"named": false
},
{
"type": "REPEAT",
"named": false
},
{
"type": "RETAIN",
"named": false
},
{
"type": "Real",
"named": false
},
{
"type": "SINT",
"named": false
},
{
"type": "SInt",
"named": false
},
{
"type": "STRING",
"named": false
},
{
"type": "String",
"named": false
},
{
"type": "THEN",
"named": false
},
{
"type": "TIME",
"named": false
},
{
"type": "TITLE",
"named": false
},
{
"type": "TO",
"named": false
},
{
"type": "TOD",
"named": false
},
{
"type": "TRUE",
"named": false
},
{
"type": "Time",
"named": false
},
{
"type": "Tod",
"named": false
},
{
"type": "UINT",
"named": false
},
{
"type": "UInt",
"named": false
},
{
"type": "UNTIL",
"named": false
},
{
"type": "USINT",
"named": false
},
{
"type": "USInt",
"named": false
},
{
"type": "VAR",
"named": false
},
{
"type": "VAR_CONSTANT",
"named": false
},
{
"type": "VAR_INPUT",
"named": false
},
{
"type": "VAR_IN_OUT",
"named": false
},
{
"type": "VAR_NON_RETAIN",
"named": false
},
{
"type": "VAR_OUTPUT",
"named": false
},
{
"type": "VAR_RETAIN",
"named": false
},
{
"type": "VAR_TEMP",
"named": false
},
{
"type": "VERSION",
"named": false
},
{
"type": "WCHAR",
"named": false
},
{
"type": "WChar",
"named": false
},
{
"type": "WHILE",
"named": false
},
{
"type": "WORD",
"named": false
},
{
"type": "WSTRING",
"named": false
},
{
"type": "WString",
"named": false
},
{
"type": "Word",
"named": false
},
{
"type": "XOR",
"named": false
},
{
"type": "[",
"named": false
@@ -1442,37 +1291,13 @@
"named": true,
"extra": true
},
{
"type": "byte",
"named": false
},
{
"type": "c_style_comment",
"named": true,
"extra": true
},
{
"type": "char",
"named": false
},
{
"type": "continue_statement",
"named": true
},
{
"type": "dint",
"named": false
},
{
"type": "dt",
"named": false
},
{
"type": "dword",
"named": false
},
{
"type": "exit_statement",
"type": "constant",
"named": true
},
{
@@ -1484,73 +1309,33 @@
"named": true
},
{
"type": "int",
"named": false
"type": "keyword",
"named": true
},
{
"type": "line_comment",
"named": true,
"extra": true
},
{
"type": "lreal",
"named": false
},
{
"type": "number",
"named": true
},
{
"type": "real",
"named": false
"type": "operator",
"named": true
},
{
"type": "sint",
"named": false
},
{
"type": "string",
"named": false
},
{
"type": "time",
"named": false
"type": "prefix",
"named": true
},
{
"type": "time_value",
"named": true
},
{
"type": "tod",
"named": false
},
{
"type": "TOD",
"named": false
},
{
"type": "uint",
"named": false
},
{
"type": "UInt",
"named": false
},
{
"type": "usint",
"named": false
},
{
"type": "wchar",
"named": false
},
{
"type": "word",
"named": false
},
{
"type": "wstring",
"named": false
"type": "type_builtin",
"named": true
},
{
"type": "{",
@@ -1560,4 +1345,4 @@
"type": "}",
"named": false
}
]
]
+58717 -54517
View File
File diff suppressed because it is too large Load Diff
+23
View File
@@ -129,6 +129,10 @@ function M.extract_variables(content)
end
end
local var_data_type = "unknown"
local var_comment = line:match("//%s*(.+)$")
if not var_comment then
var_comment = line:match("%(%*(.-)%*%)")
end
if type_start then
local type_part = line:sub(type_start + 1)
type_part = type_part:gsub("%s*:=.*$", "")
@@ -143,6 +147,7 @@ function M.extract_variables(content)
data_type = var_data_type,
line = line_num,
character = col - 1,
comment = var_comment,
}
variable_positions[var_name] = {
line = line_num,
@@ -172,6 +177,7 @@ function M.extract_types(content)
local brace_depth = 0
local in_struct = false
local pending_type_name = nil
local pending_comment = nil
local function extract_type_name(after_colon)
if not after_colon then return nil end
@@ -183,6 +189,11 @@ function M.extract_types(content)
for i, line in ipairs(lines) do
local trimmed = line:gsub("^%s+", ""):gsub("%s+$", "")
local is_comment_only = trimmed:match("^//")
if is_comment_only then
pending_comment = line:match("//%s*(.+)$")
end
local is_type_line = trimmed:match("^TYPE") and (trimmed:match("^TYPE%s") or trimmed:match('^TYPE"') or trimmed == "TYPE")
if is_type_line then
@@ -190,6 +201,9 @@ function M.extract_types(content)
in_struct = false
brace_depth = 0
pending_type_name = trimmed:match('^TYPE%s+"([^"]+)') or trimmed:match("^TYPE%s+(%w+)")
if not pending_comment then
pending_comment = trimmed:match("//%s*(.+)$")
end
elseif in_type_section and trimmed:match("^END_TYPE") then
in_type_section = false
in_struct = false
@@ -203,10 +217,12 @@ function M.extract_types(content)
kind = "struct",
fields = {},
start_line = i - 1,
comment = pending_comment,
}
types[type_name_with_struct] = current_type
in_struct = true
pending_type_name = nil
pending_comment = nil
elseif trimmed:match("^STRUCT%s*$") then
if pending_type_name then
current_type = {
@@ -214,9 +230,11 @@ function M.extract_types(content)
kind = "struct",
fields = {},
start_line = i - 1,
comment = pending_comment,
}
types[pending_type_name] = current_type
pending_type_name = nil
pending_comment = nil
end
in_struct = true
elseif trimmed:match("^END_STRUCT%s*$") then
@@ -226,9 +244,14 @@ function M.extract_types(content)
if field_name then
local after_colon = line:match(":%s*(.+)")
local field_type = after_colon and extract_type_name(after_colon)
local field_comment = line:match("//%s*(.+)$")
if not field_comment then
field_comment = line:match("%(%*(.-)%*%)")
end
table.insert(current_type.fields, {
name = field_name,
type = field_type or "unknown",
comment = field_comment,
})
end
elseif not in_struct and not pending_type_name then
+118 -46
View File
@@ -85,6 +85,13 @@ local function parse_scl_type_file(content)
local current_type_name = nil
local pending_type_name = nil
local in_fb = false
local fb_name = nil
local fb_inputs = {}
local fb_outputs = {}
local fb_inouts = {}
local current_fb_section = nil
content = content:gsub("^([\239\187\191]+)", "")
local lines = {}
@@ -93,58 +100,123 @@ local function parse_scl_type_file(content)
for i, line in ipairs(lines) do
local trimmed = line:gsub("^%s+", ""):gsub("%s+$", "")
if trimmed:match("^TYPE%s") or trimmed:match('^TYPE"') or trimmed == "TYPE" then
in_type = true
in_struct = false
current_type_name = nil
pending_type_name = nil
local type_name = trimmed:match('TYPE%s+"([^"]+)"') or trimmed:match("^TYPE%s+(%w+)")
if type_name then
pending_type_name = type_name
local fb_match = trimmed:match('^FUNCTION_BLOCK%s+"([^"]+)"') or trimmed:match("^FUNCTION_BLOCK%s+(%w+)")
if fb_match then
in_fb = true
fb_name = fb_match
fb_inputs = {}
fb_outputs = {}
fb_inouts = {}
current_fb_section = nil
elseif in_fb and trimmed:match("^END_FUNCTION_BLOCK") then
if fb_name and (#fb_inputs > 0 or #fb_outputs > 0 or #fb_inouts > 0) then
types[fb_name] = {
name = fb_name,
kind = "function_block",
inputs = fb_inputs,
outputs = fb_outputs,
inouts = fb_inouts,
source = "scl_file"
}
end
in_struct = false
current_type_name = nil
pending_type_name = nil
elseif trimmed:match("^END_TYPE") then
in_type = false
in_struct = false
current_type_name = nil
pending_type_name = nil
elseif in_type then
if trimmed:match("^END_STRUCT") or trimmed:match("^END_STRUCT;") then
in_fb = false
fb_name = nil
fb_inputs = {}
fb_outputs = {}
fb_inouts = {}
current_fb_section = nil
elseif in_fb then
if trimmed:match("^VAR_INPUT") then
current_fb_section = "input"
elseif trimmed:match("^VAR_OUTPUT") then
current_fb_section = "output"
elseif trimmed:match("^VAR_IN_OUT") then
current_fb_section = "inout"
elseif trimmed:match("^VAR%s") or trimmed:match("^VAR%s*$") then
current_fb_section = nil
elseif trimmed:match("^VAR_TEMP") then
current_fb_section = nil
elseif trimmed:match("^END_VAR") then
current_fb_section = nil
elseif current_fb_section then
local field_name = line:match("^%s*(%w+)")
if field_name and not trimmed:match("^%(%*") then
local field_type = line:match(":%s*([^;]+)")
if field_type then
local attr_match = field_type:match("%{%s*[^%}]+%%s*%}%s*:%s*(.+)$")
if attr_match then
field_type = attr_match
end
field_type = field_type:gsub("%s*;.*$", "")
field_type = field_type:gsub("^%s+", ""):gsub("%s+$", "")
local field_comment = line:match("//%s*(.+)$") or line:match("%(%*(.-)%*%)")
local field = { name = field_name, type = field_type, comment = field_comment }
if current_fb_section == "input" then
table.insert(fb_inputs, field)
elseif current_fb_section == "output" then
table.insert(fb_outputs, field)
elseif current_fb_section == "inout" then
table.insert(fb_inouts, field)
end
end
end
end
end
if not in_fb then
if trimmed:match("^TYPE%s") or trimmed:match('^TYPE"') or trimmed == "TYPE" then
in_type = true
in_struct = false
current_type_name = nil
pending_type_name = nil
elseif trimmed:match("^STRUCT") then
if pending_type_name then
current_type_name = pending_type_name
in_struct = true
types[pending_type_name] = { name = pending_type_name, kind = "struct", fields = {}, source = "data_types_folder" }
pending_type_name = nil
end
elseif not in_struct and trimmed:match("^%w+%s*:") and not trimmed:match("^STRUCT") then
local type_name = trimmed:match("^(%w+)%s*:")
local type_name = trimmed:match('TYPE%s+"([^"]+)"') or trimmed:match("^TYPE%s+(%w+)")
if type_name then
local next_line = lines[i + 1]
local next_trimmed = next_line and next_line:gsub("^%s+", ""):gsub("%s+$", "")
if next_trimmed and next_trimmed:match("^STRUCT") then
pending_type_name = type_name
else
pending_type_name = nil
current_type_name = nil
in_struct = false
local base_type = trimmed:match(":%s*([%w_]+)")
types[type_name] = { name = type_name, kind = "alias", base_type = base_type or "UNKNOWN", source = "data_types_folder" }
end
pending_type_name = type_name
end
elseif in_struct and current_type_name and types[current_type_name] then
local field_name = line:match("^%s*(%w+)%s*:")
if field_name and field_name ~= "END_STRUCT" then
local field_type = line:match(":%s*([%w_]+)")
table.insert(types[current_type_name].fields, {
name = field_name,
type = field_type or "unknown",
})
in_struct = false
current_type_name = nil
pending_type_name = nil
elseif trimmed:match("^END_TYPE") then
in_type = false
in_struct = false
current_type_name = nil
pending_type_name = nil
elseif in_type then
if trimmed:match("^END_STRUCT") or trimmed:match("^END_STRUCT;") then
in_struct = false
current_type_name = nil
pending_type_name = nil
elseif trimmed:match("^STRUCT") then
if pending_type_name then
current_type_name = pending_type_name
in_struct = true
types[pending_type_name] = { name = pending_type_name, kind = "struct", fields = {}, source = "data_types_folder" }
pending_type_name = nil
end
elseif not in_struct and trimmed:match("^%w+%s*:") and not trimmed:match("^STRUCT") then
local type_name = trimmed:match("^(%w+)%s*:")
if type_name then
local next_line = lines[i + 1]
local next_trimmed = next_line and next_line:gsub("^%s+", ""):gsub("%s+$", "")
if next_trimmed and next_trimmed:match("^STRUCT") then
pending_type_name = type_name
else
pending_type_name = nil
current_type_name = nil
in_struct = false
local base_type = trimmed:match(":%s*([%w_]+)")
types[type_name] = { name = type_name, kind = "alias", base_type = base_type or "UNKNOWN", source = "data_types_folder" }
end
end
elseif in_struct and current_type_name and types[current_type_name] then
local field_name = line:match("^%s*(%w+)%s*:")
if field_name and field_name ~= "END_STRUCT" then
local field_type = line:match(":%s*([%w_]+)")
table.insert(types[current_type_name].fields, {
name = field_name,
type = field_type or "unknown",
})
end
end
end
end