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
+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