feat: add builtin types/functions to diagnostics and completions
Files Modified/Created: - src/builtin_instructions.lua - NEW: Contains all TIA Portal built-in types, FBs, and functions - src/diagnostics.lua - Added missing data types + builtin instruction checking - src/main.lua - Added completion support for built-in types/F Bs/functions - src/plc_json.lua - Added .db file parsing + BOM handling fixes - lua/scl/blink_cmp_source.lua - Extended completion with all built-in types/F Bs/functions - src/node-types.json - Added node types for parser - AGENTS.md - Updated documentation Features Added: 1. Diagnostics - No more false "Unknown data type" warnings for: - All elementary types (USINT, SINT, UINT, UDINT, LINT, ULINT, TOD, DTL, etc.) - Timer/counter types (IEC_TON, TOF, TP, CTU, CTD, CTUD) - TIA Portal built-in FBs and functions 2. Auto-completion - Shows in VAR section after :: - 28 elementary data types - 23 built-in function blocks (TON, TOF, TP, CTU, CTD, CTUD, R_TRIG, F_TRIG, etc.) - 80+ built-in functions (ADD, SUB, MUL, DIV, SIN, COS, SQRT, etc.)
This commit is contained in:
@@ -30,7 +30,8 @@ scl_lsp/
|
|||||||
│ ├── diagnostics.lua # Linter
|
│ ├── diagnostics.lua # Linter
|
||||||
│ ├── formatter.lua # Document formatter
|
│ ├── formatter.lua # Document formatter
|
||||||
│ ├── treesitter.lua # Tree-sitter integration
|
│ ├── treesitter.lua # Tree-sitter integration
|
||||||
│ ├── plc_json.lua # External UDT loading
|
│ ├── plc_json.lua # External UDT/DB loading
|
||||||
|
│ ├── builtin_instructions.lua # TIA Portal built-in types/FBs/functions
|
||||||
│ └── json.lua # JSON encoder/decoder
|
│ └── json.lua # JSON encoder/decoder
|
||||||
├── lua/scl/ # Neovim plugin (uses require)
|
├── lua/scl/ # Neovim plugin (uses require)
|
||||||
│ ├── init.lua # Main plugin setup
|
│ ├── init.lua # Main plugin setup
|
||||||
@@ -155,12 +156,30 @@ if ok and module then
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Diagnostics
|
||||||
|
|
||||||
|
The LSP validates data types and provides warnings for unknown types. It recognizes:
|
||||||
|
- **Elementary data types** - BOOL, BYTE, WORD, DWORD, LWORD, CHAR, WCHAR, STRING, WSTRING, INT, DINT, LINT, USINT, SINT, UINT, UDINT, ULINT, REAL, LREAL, TIME, DATE, TOD, TIME_OF_DAY, DATE_AND_TIME, DT, S5TIME, LTIME, DTL
|
||||||
|
- **Timer/counter types** - IEC_TIMER, TON_TIME, TOF_TIME, TONR_TIME, TP_TIME, CTU, CTD, CTUD
|
||||||
|
- **TIA Portal built-in instructions** - Functions and function blocks
|
||||||
|
- **Workspace types** - UDTs from `.udt` files and data blocks from `.db` files
|
||||||
|
|
||||||
## Completion Triggers
|
## Completion Triggers
|
||||||
- `#` - Local variables (after BEGIN)
|
- `#` - Local variables (after BEGIN)
|
||||||
- `.` - Member access (UDT/DB)
|
- `.` - Member access (UDT/DB)
|
||||||
- `"` - Global DB names
|
- `"` - Global DB names
|
||||||
- `(` - FB/Function parameters
|
- `(` - FB/Function parameters
|
||||||
- ` ` (space) - General completion
|
- ` ` (space) - General completion
|
||||||
|
- `:` - Type declaration (in VAR section)
|
||||||
|
|
||||||
|
### Completion Types
|
||||||
|
The LSP provides auto-completion for:
|
||||||
|
- **Local variables** - Variables declared in VAR sections
|
||||||
|
- **UDT types** - User-defined types from `.udt` files
|
||||||
|
- **Global DBs** - Data blocks from `.db` files
|
||||||
|
- **Elementary types** - BOOL, INT, DINT, REAL, TIME, etc.
|
||||||
|
- **TIA Portal built-in FBs** - TON, TOF, TP, CTU, CTD, CTUD, R_TRIG, etc.
|
||||||
|
- **TIA Portal built-in functions** - ADD, SUB, MUL, DIV, SIN, COS, SQRT, etc.
|
||||||
|
|
||||||
## Formatter Options
|
## Formatter Options
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,249 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.FUNCTIONS = {
|
||||||
|
ADD = true,
|
||||||
|
SUB = true,
|
||||||
|
MUL = true,
|
||||||
|
DIV = true,
|
||||||
|
MOD = true,
|
||||||
|
ABS = true,
|
||||||
|
NEG = true,
|
||||||
|
SQR = true,
|
||||||
|
SQRT = true,
|
||||||
|
EXP = true,
|
||||||
|
LN = true,
|
||||||
|
LOG = true,
|
||||||
|
SIN = true,
|
||||||
|
COS = true,
|
||||||
|
TAN = true,
|
||||||
|
ASIN = true,
|
||||||
|
ACOS = true,
|
||||||
|
ATAN = true,
|
||||||
|
MIN = true,
|
||||||
|
MAX = true,
|
||||||
|
LIMIT = true,
|
||||||
|
CALCULATE = true,
|
||||||
|
CONVERT = true,
|
||||||
|
ROUND = true,
|
||||||
|
TRUNC = true,
|
||||||
|
CEIL = true,
|
||||||
|
FLOOR = true,
|
||||||
|
SCALE_X = true,
|
||||||
|
NORM_X = true,
|
||||||
|
LEN = true,
|
||||||
|
CONCAT = true,
|
||||||
|
LEFT = true,
|
||||||
|
RIGHT = true,
|
||||||
|
MID = true,
|
||||||
|
INSERT = true,
|
||||||
|
DELETE = true,
|
||||||
|
REPLACE = true,
|
||||||
|
FIND = true,
|
||||||
|
AND = true,
|
||||||
|
OR = true,
|
||||||
|
XOR = true,
|
||||||
|
NOT = true,
|
||||||
|
SHL = true,
|
||||||
|
SHR = true,
|
||||||
|
ROL = true,
|
||||||
|
ROR = true,
|
||||||
|
DECO = true,
|
||||||
|
ENCO = true,
|
||||||
|
BCD_I = true,
|
||||||
|
I_BCD = true,
|
||||||
|
DI_BCD = true,
|
||||||
|
DI_R = true,
|
||||||
|
I_DI = true,
|
||||||
|
R_DI = true,
|
||||||
|
R_DB = true,
|
||||||
|
MOVE = true,
|
||||||
|
FILL = true,
|
||||||
|
SWAP = true,
|
||||||
|
PEEK = true,
|
||||||
|
POKE = true,
|
||||||
|
GET_ERROR = true,
|
||||||
|
GET_ERR_ID = true,
|
||||||
|
RESET_ERR = true,
|
||||||
|
T_CONV = true,
|
||||||
|
T_ADD = true,
|
||||||
|
T_SUB = true,
|
||||||
|
T_DIFF = true,
|
||||||
|
T_COMBINE = true,
|
||||||
|
DT_DATE = true,
|
||||||
|
DT_TOD = true,
|
||||||
|
DATE_TO_DTL = true,
|
||||||
|
DTL_TO_DATE = true,
|
||||||
|
TOD_TO_DTL = true,
|
||||||
|
DTL_TO_TOD = true,
|
||||||
|
TimeToTicks = true,
|
||||||
|
TicksToTime = true,
|
||||||
|
GETHIGHNUM = true,
|
||||||
|
GETLOWNUM = true,
|
||||||
|
SAMPLE = true,
|
||||||
|
SAMPLE_TRIG = true,
|
||||||
|
WRIT_HIST = true,
|
||||||
|
READ_HIST = true,
|
||||||
|
COMPRESS = true,
|
||||||
|
READFILE = true,
|
||||||
|
WRITEFILE = true,
|
||||||
|
READ_DB = true,
|
||||||
|
WRITE_DB = true,
|
||||||
|
RDREC = true,
|
||||||
|
WRREC = true,
|
||||||
|
RALRM = true,
|
||||||
|
GET_DIAG = true,
|
||||||
|
DPRINT = true,
|
||||||
|
CTRL_PWM = true,
|
||||||
|
CTRL_PTO = true,
|
||||||
|
STATUS_PTO = true,
|
||||||
|
HOME = true,
|
||||||
|
MOVE_JOG = true,
|
||||||
|
MOVE_VEL = true,
|
||||||
|
MOVE_AXIS = true,
|
||||||
|
POS_PULSE = true,
|
||||||
|
POS_MC = true,
|
||||||
|
SET_CAM = true,
|
||||||
|
CAM_TB = true,
|
||||||
|
CAM_TE = true,
|
||||||
|
CAM_DOWN = true,
|
||||||
|
MC_CamIn = true,
|
||||||
|
MC_CamOut = true,
|
||||||
|
MC_Superimpose = true,
|
||||||
|
MC_GearIn = true,
|
||||||
|
MC_GearOut = true,
|
||||||
|
MC_Phasing = true,
|
||||||
|
MC_CombineAxes = true,
|
||||||
|
MC_Interpolate = true,
|
||||||
|
MC_MoveLinear = true,
|
||||||
|
MC_MoveCircular = true,
|
||||||
|
MC_MoveDirect = true,
|
||||||
|
MC_Home = true,
|
||||||
|
MC_Halt = true,
|
||||||
|
MC_Stop = true,
|
||||||
|
MC_Power = true,
|
||||||
|
MC_Reset = true,
|
||||||
|
MC_ReadParam = true,
|
||||||
|
MC_WriteParam = true,
|
||||||
|
MC_ReadAxisError = true,
|
||||||
|
MC_TouchProbe = true,
|
||||||
|
MC_AbortTrigger = true,
|
||||||
|
MC_ExtSetPointGen = true,
|
||||||
|
MC_Generator = true,
|
||||||
|
MC_BRIDGE = true,
|
||||||
|
MC_BRIDGE_OFF = true,
|
||||||
|
ENABLE = true,
|
||||||
|
DISABLE = true,
|
||||||
|
DISPLAY = true,
|
||||||
|
LOG_EVENT = true,
|
||||||
|
ALARM = true,
|
||||||
|
ALARM_DQ = true,
|
||||||
|
GET_ALARM = true,
|
||||||
|
GETINST = true,
|
||||||
|
GETALARMST = true,
|
||||||
|
CHANGEOVER = true,
|
||||||
|
REPLY_VALUE = true,
|
||||||
|
NOTIFY = true,
|
||||||
|
NOTIFY_WITH_ACK = true,
|
||||||
|
ASGEN = true,
|
||||||
|
HMI_R_DISCONNECT = true,
|
||||||
|
HMI_R_CONNECT = true,
|
||||||
|
HMI_R_SEND = true,
|
||||||
|
HMI_R_RECEIVE = true,
|
||||||
|
HMI_R_GET_STATE = true,
|
||||||
|
MODBUSPN = true,
|
||||||
|
MODBUSCP = true,
|
||||||
|
TSEND = true,
|
||||||
|
TRCV = true,
|
||||||
|
TCON = true,
|
||||||
|
TDISCON = true,
|
||||||
|
TSEND_C = true,
|
||||||
|
TRCV_C = true,
|
||||||
|
TUSEND = true,
|
||||||
|
TURCV = true,
|
||||||
|
TCONNECT = true,
|
||||||
|
TDISCONNECT = true,
|
||||||
|
GETHNSON = true,
|
||||||
|
DELHNSON = true,
|
||||||
|
DNS_CLIENT = true,
|
||||||
|
DHCP_CLIENT = true,
|
||||||
|
DCP_CLIENT = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
M.FUNCTION_BLOCKS = {
|
||||||
|
TON = true,
|
||||||
|
TOF = true,
|
||||||
|
TP = true,
|
||||||
|
TONR = true,
|
||||||
|
CTU = true,
|
||||||
|
CTD = true,
|
||||||
|
CTUD = true,
|
||||||
|
IEC_TIMER = true,
|
||||||
|
IEC_TON = true,
|
||||||
|
IEC_TOF = true,
|
||||||
|
IEC_TP = true,
|
||||||
|
IEC_COUNTER = true,
|
||||||
|
IEC_CTU = true,
|
||||||
|
IEC_CTD = true,
|
||||||
|
IEC_CTUD = true,
|
||||||
|
R_TRIG = true,
|
||||||
|
F_TRIG = true,
|
||||||
|
SR = true,
|
||||||
|
RS = true,
|
||||||
|
SCHEDULE = true,
|
||||||
|
BPM = true,
|
||||||
|
LOG = true,
|
||||||
|
DATALOG = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
M.DATA_TYPES = {
|
||||||
|
BOOL = true,
|
||||||
|
BYTE = true,
|
||||||
|
WORD = true,
|
||||||
|
DWORD = true,
|
||||||
|
LWORD = true,
|
||||||
|
CHAR = true,
|
||||||
|
WCHAR = true,
|
||||||
|
STRING = true,
|
||||||
|
WSTRING = true,
|
||||||
|
INT = true,
|
||||||
|
DINT = true,
|
||||||
|
LINT = true,
|
||||||
|
USINT = true,
|
||||||
|
SINT = true,
|
||||||
|
UINT = true,
|
||||||
|
UDINT = true,
|
||||||
|
ULINT = true,
|
||||||
|
REAL = true,
|
||||||
|
LREAL = true,
|
||||||
|
TIME = true,
|
||||||
|
DATE = true,
|
||||||
|
TOD = true,
|
||||||
|
TIME_OF_DAY = true,
|
||||||
|
DATE_AND_TIME = true,
|
||||||
|
DT = true,
|
||||||
|
S5TIME = true,
|
||||||
|
LTIME = true,
|
||||||
|
DTL = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
function M.is_known_function(name)
|
||||||
|
return M.FUNCTIONS[name:upper()] or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.is_known_function_block(name)
|
||||||
|
return M.FUNCTION_BLOCKS[name:upper()] or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.is_known_data_type(name)
|
||||||
|
return M.DATA_TYPES[name:upper()] or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.is_known_instruction(name)
|
||||||
|
return M.is_known_function(name) or M.is_known_function_block(name) or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.is_known_type_or_instruction(name)
|
||||||
|
return M.is_known_data_type(name) or M.is_known_instruction(name) or false
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
+9
-1
@@ -16,6 +16,7 @@ end
|
|||||||
package.path = package.path .. ";" .. script_path .. "/?.lua"
|
package.path = package.path .. ";" .. script_path .. "/?.lua"
|
||||||
|
|
||||||
local parser = dofile(script_path .. "/parser.lua")
|
local parser = dofile(script_path .. "/parser.lua")
|
||||||
|
local builtin = dofile(script_path .. "/builtin_instructions.lua")
|
||||||
|
|
||||||
local DiagnosticSeverity = {
|
local DiagnosticSeverity = {
|
||||||
Error = 1,
|
Error = 1,
|
||||||
@@ -156,9 +157,16 @@ function M.validate_diagnostics(content, workspace_types, root_dir)
|
|||||||
upper_type ~= "WSTRING" and upper_type ~= "TIME" and upper_type ~= "DATE" and
|
upper_type ~= "WSTRING" and upper_type ~= "TIME" and upper_type ~= "DATE" and
|
||||||
upper_type ~= "TIME_OF_DAY" and upper_type ~= "DATE_AND_TIME" and
|
upper_type ~= "TIME_OF_DAY" and upper_type ~= "DATE_AND_TIME" and
|
||||||
upper_type ~= "S5TIME" and upper_type ~= "LTIME" and
|
upper_type ~= "S5TIME" and upper_type ~= "LTIME" and
|
||||||
|
upper_type ~= "USINT" and upper_type ~= "SINT" and upper_type ~= "UINT" and
|
||||||
|
upper_type ~= "UDINT" and upper_type ~= "LINT" and upper_type ~= "ULINT" and
|
||||||
|
upper_type ~= "TOD" and upper_type ~= "DTL" and
|
||||||
upper_type ~= "IEC_TIMER" and upper_type ~= "TON_TIME" and
|
upper_type ~= "IEC_TIMER" and upper_type ~= "TON_TIME" and
|
||||||
upper_type ~= "TOF_TIME" and upper_type ~= "TONR_TIME" and
|
upper_type ~= "TOF_TIME" and upper_type ~= "TONR_TIME" and
|
||||||
upper_type ~= "TP_TIME" then
|
upper_type ~= "TP_TIME" and upper_type ~= "IEC_TON" and
|
||||||
|
upper_type ~= "IEC_TOF" and upper_type ~= "IEC_TP" and
|
||||||
|
upper_type ~= "IEC_COUNTER" and upper_type ~= "CTU" and
|
||||||
|
upper_type ~= "CTD" and upper_type ~= "CTUD" and
|
||||||
|
not builtin.is_known_type_or_instruction(upper_type) then
|
||||||
table.insert(diagnostics, {
|
table.insert(diagnostics, {
|
||||||
range = range_to_lsp(line_num, 0, line_num, #line),
|
range = range_to_lsp(line_num, 0, line_num, #line),
|
||||||
severity = DiagnosticSeverity.Warning,
|
severity = DiagnosticSeverity.Warning,
|
||||||
|
|||||||
+70
-1
@@ -26,6 +26,7 @@ local treesitter = dofile(script_path .. "/treesitter.lua")
|
|||||||
local plc_json = dofile(script_path .. "/plc_json.lua")
|
local plc_json = dofile(script_path .. "/plc_json.lua")
|
||||||
local diagnostics = dofile(script_path .. "/diagnostics.lua")
|
local diagnostics = dofile(script_path .. "/diagnostics.lua")
|
||||||
local formatter = dofile(script_path .. "/formatter.lua")
|
local formatter = dofile(script_path .. "/formatter.lua")
|
||||||
|
local builtin = dofile(script_path .. "/builtin_instructions.lua")
|
||||||
|
|
||||||
local documents = {}
|
local documents = {}
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ local semanticTokenTypes = {
|
|||||||
local capabilities = {
|
local capabilities = {
|
||||||
hoverProvider = true,
|
hoverProvider = true,
|
||||||
completionProvider = {
|
completionProvider = {
|
||||||
triggerCharacters = { ".", "#", "(" },
|
triggerCharacters = { ".", "#", "(", " ", ":", ";", "\n" },
|
||||||
resolveProvider = false,
|
resolveProvider = false,
|
||||||
},
|
},
|
||||||
definitionProvider = true,
|
definitionProvider = true,
|
||||||
@@ -385,6 +386,27 @@ function handlers.textDocument_completion(params)
|
|||||||
local line = lines[line_idx]
|
local line = lines[line_idx]
|
||||||
local col = params.position.character
|
local col = params.position.character
|
||||||
local trigger = col > 0 and line:sub(col, col) or ""
|
local trigger = col > 0 and line:sub(col, col) or ""
|
||||||
|
local line_before = col > 1 and line:sub(1, col - 1) or ""
|
||||||
|
|
||||||
|
-- Get word prefix being typed (for filtering)
|
||||||
|
local prefix_start = col - 1
|
||||||
|
while prefix_start > 0 and line:sub(prefix_start, prefix_start):match("[%w_]") do
|
||||||
|
prefix_start = prefix_start - 1
|
||||||
|
end
|
||||||
|
local word_prefix = line:sub(prefix_start + 1, col - 1)
|
||||||
|
|
||||||
|
-- Check if we're in VAR section
|
||||||
|
local in_var_section = false
|
||||||
|
for i = line_idx - 1, 1, -1 do
|
||||||
|
local prev_line = lines[i]:gsub("^%s+", ""):gsub("%s+$", "")
|
||||||
|
if prev_line:match("^END_VAR") or prev_line:match("^VAR_TEMP") or prev_line:match("^VAR_IN_OUT") or prev_line:match("^VAR_OUTPUT") or prev_line:match("^VAR_INPUT") then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if prev_line:match("^VAR%s*$") or prev_line:match("^VAR$") then
|
||||||
|
in_var_section = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if trigger == "#" then
|
if trigger == "#" then
|
||||||
if doc.variables then
|
if doc.variables then
|
||||||
@@ -450,6 +472,53 @@ function handlers.textDocument_completion(params)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
-- Check context before cursor
|
||||||
|
local is_var_decl = line_before:match(":%s*$")
|
||||||
|
|
||||||
|
-- Helper to add item with prefix filtering
|
||||||
|
local function add_item(name, kind_val, detail_text)
|
||||||
|
if word_prefix == "" or name:upper():find("^" .. word_prefix:upper()) then
|
||||||
|
table.insert(items, {
|
||||||
|
label = name,
|
||||||
|
kind = kind_val,
|
||||||
|
detail = detail_text,
|
||||||
|
insertText = name,
|
||||||
|
insertTextFormat = 1,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if in_var_section or is_var_decl then
|
||||||
|
-- In VAR section: show data types and function blocks
|
||||||
|
for type_name, _ in pairs(builtin.DATA_TYPES) do
|
||||||
|
add_item(type_name, 7, "Data type")
|
||||||
|
end
|
||||||
|
|
||||||
|
for fb_name, _ in pairs(builtin.FUNCTION_BLOCKS) do
|
||||||
|
add_item(fb_name, 6, "Function block")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Also add workspace types if available
|
||||||
|
if doc.types then
|
||||||
|
for type_name, type_info in pairs(doc.types) do
|
||||||
|
add_item(type_name, 7, type_info.kind or "Type")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- General context: show all
|
||||||
|
for type_name, _ in pairs(builtin.DATA_TYPES) do
|
||||||
|
add_item(type_name, 7, "Data type")
|
||||||
|
end
|
||||||
|
|
||||||
|
for fb_name, _ in pairs(builtin.FUNCTION_BLOCKS) do
|
||||||
|
add_item(fb_name, 6, "Function block")
|
||||||
|
end
|
||||||
|
|
||||||
|
for func_name, _ in pairs(builtin.FUNCTIONS) do
|
||||||
|
add_item(func_name, 3, "Function")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -1524,10 +1524,18 @@
|
|||||||
"type": "tod",
|
"type": "tod",
|
||||||
"named": false
|
"named": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "TOD",
|
||||||
|
"named": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "uint",
|
"type": "uint",
|
||||||
"named": false
|
"named": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "UInt",
|
||||||
|
"named": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "usint",
|
"type": "usint",
|
||||||
"named": false
|
"named": false
|
||||||
@@ -1552,4 +1560,4 @@
|
|||||||
"type": "}",
|
"type": "}",
|
||||||
"named": false
|
"named": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+68
-2
@@ -85,24 +85,33 @@ local function parse_scl_type_file(content)
|
|||||||
local current_type_name = nil
|
local current_type_name = nil
|
||||||
local pending_type_name = nil
|
local pending_type_name = nil
|
||||||
|
|
||||||
|
content = content:gsub("^([\239\187\191]+)", "")
|
||||||
|
|
||||||
local lines = {}
|
local lines = {}
|
||||||
for line in content:gmatch("[^\n]+") do table.insert(lines, line) end
|
for line in content:gmatch("[^\n]+") do table.insert(lines, line) end
|
||||||
|
|
||||||
for i, line in ipairs(lines) do
|
for i, line in ipairs(lines) do
|
||||||
local trimmed = line:gsub("^%s+", ""):gsub("%s+$", "")
|
local trimmed = line:gsub("^%s+", ""):gsub("%s+$", "")
|
||||||
|
|
||||||
if trimmed:match("^TYPE%s*:?%s*$") then
|
if trimmed:match("^TYPE%s") or trimmed:match('^TYPE"') or trimmed == "TYPE" then
|
||||||
in_type = true
|
in_type = true
|
||||||
in_struct = false
|
in_struct = false
|
||||||
current_type_name = nil
|
current_type_name = nil
|
||||||
pending_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
|
||||||
|
end
|
||||||
|
in_struct = false
|
||||||
|
current_type_name = nil
|
||||||
|
pending_type_name = nil
|
||||||
elseif trimmed:match("^END_TYPE") then
|
elseif trimmed:match("^END_TYPE") then
|
||||||
in_type = false
|
in_type = false
|
||||||
in_struct = false
|
in_struct = false
|
||||||
current_type_name = nil
|
current_type_name = nil
|
||||||
pending_type_name = nil
|
pending_type_name = nil
|
||||||
elseif in_type then
|
elseif in_type then
|
||||||
if trimmed:match("^END_STRUCT") then
|
if trimmed:match("^END_STRUCT") or trimmed:match("^END_STRUCT;") then
|
||||||
in_struct = false
|
in_struct = false
|
||||||
current_type_name = nil
|
current_type_name = nil
|
||||||
pending_type_name = nil
|
pending_type_name = nil
|
||||||
@@ -144,6 +153,48 @@ local function parse_scl_type_file(content)
|
|||||||
return types
|
return types
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function parse_db_file(content)
|
||||||
|
local types = {}
|
||||||
|
local in_var = false
|
||||||
|
local current_type_name = nil
|
||||||
|
|
||||||
|
content = content:gsub("^([\239\187\191]+)", "")
|
||||||
|
|
||||||
|
local lines = {}
|
||||||
|
for line in content:gmatch("[^\n]+") do table.insert(lines, line) end
|
||||||
|
|
||||||
|
for i, line in ipairs(lines) do
|
||||||
|
local trimmed = line:gsub("^%s+", ""):gsub("%s+$", "")
|
||||||
|
|
||||||
|
if trimmed:match('^DATA_BLOCK%s+"([^"]+)"') then
|
||||||
|
current_type_name = trimmed:match('^DATA_BLOCK%s+"([^"]+)"')
|
||||||
|
types[current_type_name] = {
|
||||||
|
name = current_type_name,
|
||||||
|
kind = "struct",
|
||||||
|
fields = {},
|
||||||
|
source = "db_file",
|
||||||
|
}
|
||||||
|
elseif trimmed:match("^END_VAR") then
|
||||||
|
in_var = false
|
||||||
|
elseif trimmed:match("^VAR%s*$") or trimmed:match("^VAR ") then
|
||||||
|
in_var = true
|
||||||
|
elseif in_var and current_type_name and types[current_type_name] and trimmed ~= "" and not trimmed:match("^//") then
|
||||||
|
local field_name = line:match("^%s*(%w+)%s*:")
|
||||||
|
if field_name then
|
||||||
|
local field_type = line:match(":%s*([%w_]+)")
|
||||||
|
if field_type then
|
||||||
|
table.insert(types[current_type_name].fields, {
|
||||||
|
name = field_name,
|
||||||
|
type = field_type,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return types
|
||||||
|
end
|
||||||
|
|
||||||
function M.load_types_from_workspace(root_dir)
|
function M.load_types_from_workspace(root_dir)
|
||||||
local types = {}
|
local types = {}
|
||||||
|
|
||||||
@@ -213,6 +264,21 @@ function M.load_types_from_workspace(root_dir)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Also scan for .db files
|
||||||
|
local db_files = scan_files_recursive(root_dir, "*.db")
|
||||||
|
for _, filepath in ipairs(db_files) do
|
||||||
|
local content = run_command("cat " .. filepath:gsub(" ", "\\ "))
|
||||||
|
if content then
|
||||||
|
local db_types = parse_db_file(content)
|
||||||
|
for name, typ in pairs(db_types) do
|
||||||
|
typ.source = "db_file"
|
||||||
|
if not types[name] then
|
||||||
|
types[name] = typ
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return types
|
return types
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user