diff --git a/src/main.lua b/src/main.lua index 9853157..886f6f5 100644 --- a/src/main.lua +++ b/src/main.lua @@ -558,7 +558,7 @@ function handlers.textDocument_declaration(params) local var_type = doc.variables[word].data_type if var_type then var_type = var_type:gsub("%s*;.*$", ""):gsub("%s*:=.*$", ""):gsub("^%s+", ""):gsub("%s+$", "") - local type_name = var_type:match("([%w_]+)") + local type_name = var_type:match('"([^"]+)"') or var_type:match("([%w_]+)") if type_name and type_name ~= word then table.insert(search_names, type_name) end diff --git a/src/parser.lua b/src/parser.lua index 06bb44c..95ec23e 100644 --- a/src/parser.lua +++ b/src/parser.lua @@ -11,6 +11,7 @@ function M.extract_variables(content) end local in_var_section = false + local in_struct_section = false local var_type = "unknown" local current_block = nil @@ -22,6 +23,7 @@ function M.extract_variables(content) if fb_name then current_block = { name = fb_name, kind = "function_block" } in_var_section = false + in_struct_section = false return end @@ -29,6 +31,7 @@ function M.extract_variables(content) if ob_name then current_block = { name = ob_name, kind = "organization_block" } in_var_section = false + in_struct_section = false return end @@ -36,6 +39,7 @@ function M.extract_variables(content) if fn_name then current_block = { name = fn_name, kind = "function" } in_var_section = false + in_struct_section = false return end @@ -44,6 +48,19 @@ function M.extract_variables(content) trimmed:match("^END_FUNCTION") then current_block = nil in_var_section = false + in_struct_section = false + return + end + + -- Handle TYPE sections (for .udt files with STRUCT) + if trimmed:match("^TYPE") and (trimmed:match("^TYPE%s") or trimmed:match('^TYPE"') or trimmed == "TYPE") then + in_struct_section = true + var_type = "STRUCT_FIELD" + return + end + + if trimmed:match("^END_TYPE") then + in_struct_section = false return end @@ -56,6 +73,7 @@ function M.extract_variables(content) trimmed:match("^VAR[_ ]*RETAIN%s*$") or trimmed:match("^VAR[_ ]*NON_RETAIN%s*$") then in_var_section = true + in_struct_section = false if trimmed:match("INPUT") then var_type = "VAR_INPUT" elseif trimmed:match("OUTPUT") then var_type = "VAR_OUTPUT" elseif trimmed:match("IN_OUT") then var_type = "VAR_IN_OUT" @@ -72,7 +90,18 @@ function M.extract_variables(content) return end - if not in_var_section then return end + if trimmed:match("^STRUCT%s*$") then + in_struct_section = true + var_type = "STRUCT_FIELD" + return + end + + if trimmed:match("^END_STRUCT%s*$") then + in_struct_section = false + return + end + + if not in_var_section and not in_struct_section then return end if trimmed:match("^%(%*") or trimmed:match("^%s*//") then return @@ -96,7 +125,7 @@ function M.extract_variables(content) brace_depth = brace_depth - 1 elseif c == ":" and brace_depth == 0 then type_start = i - break -- Only take the first : outside braces + break end end local var_data_type = "unknown" @@ -141,70 +170,90 @@ function M.extract_types(content) local in_type_section = false local current_type = nil local brace_depth = 0 + local in_struct = false + local pending_type_name = nil + + local function extract_type_name(after_colon) + if not after_colon then return nil end + local quoted = after_colon:match('^%s*"([^"]+)"') + if quoted then return quoted end + return after_colon:match("^%s*(%w+)") + end for i, line in ipairs(lines) do local trimmed = line:gsub("^%s+", ""):gsub("%s+$", "") - if trimmed:match("^TYPE$") or trimmed:match("^TYPE%s+:=") then + local is_type_line = trimmed:match("^TYPE") and (trimmed:match("^TYPE%s") or trimmed:match('^TYPE"') or trimmed == "TYPE") + + if is_type_line then in_type_section = true + in_struct = false brace_depth = 0 + pending_type_name = trimmed:match('^TYPE%s+"([^"]+)') or trimmed:match("^TYPE%s+(%w+)") elseif in_type_section and trimmed:match("^END_TYPE") then in_type_section = false + in_struct = false current_type = nil + pending_type_name = nil elseif in_type_section then - local type_name = trimmed:match("^(%w+):") - if type_name then - local struct_start = trimmed:find("{") - local array_match = trimmed:find("ARRAY") - - if struct_start then + local type_name_with_struct = trimmed:match("^(%w+)%s+.-:%s+STRUCT") + if type_name_with_struct then + current_type = { + name = type_name_with_struct, + kind = "struct", + fields = {}, + start_line = i - 1, + } + types[type_name_with_struct] = current_type + in_struct = true + pending_type_name = nil + elseif trimmed:match("^STRUCT%s*$") then + if pending_type_name then current_type = { - name = type_name, + name = pending_type_name, kind = "struct", fields = {}, start_line = i - 1, } - types[type_name] = current_type - elseif array_match then - local of_type = trimmed:match("OF%s+(%w+)") - if of_type then - current_type = { - name = type_name, - kind = "array", - element_type = of_type, - start_line = i - 1, - } - types[type_name] = current_type - end - else - local field_type = trimmed:match(":%s*(%w+)") - if field_type then - current_type = { - name = type_name, - kind = "alias", - base_type = field_type, - start_line = i - 1, - } - types[type_name] = current_type - end + types[pending_type_name] = current_type + pending_type_name = nil end - end - - if current_type and current_type.kind == "struct" then - for open in line:gmatch("{") do brace_depth = brace_depth + 1 end - for close in line:gmatch("}") do brace_depth = brace_depth - 1 end - - local field_name = line:match("^%s*(%w+):") - if field_name and field_name ~= current_type.name then - local field_type = line:match(":%s*(%w+)") + in_struct = true + elseif trimmed:match("^END_STRUCT%s*$") then + in_struct = false + elseif in_struct and current_type and current_type.fields then + local field_name = line:match("^%s*(%w+)%s*:%s*") + if field_name then + local after_colon = line:match(":%s*(.+)") + local field_type = after_colon and extract_type_name(after_colon) table.insert(current_type.fields, { name = field_name, type = field_type or "unknown", }) end - - if brace_depth == 0 then - current_type = nil + elseif not in_struct and not pending_type_name then + local array_match = trimmed:match("^(%w+)%s+:%s+ARRAY") + if array_match then + local of_type = trimmed:match("OF%s+(%w+)") + current_type = { + name = array_match, + kind = "array", + element_type = of_type, + start_line = i - 1, + } + types[array_match] = current_type + else + local alias_match = trimmed:match("^(%w+)%s+:%s+(%w+)") + if alias_match then + local base_type = extract_type_name(trimmed:match(":%s+(.+)$")) + current_type = { + name = alias_match, + kind = "alias", + base_type = base_type, + start_line = i - 1, + } + types[alias_match] = current_type + end end end end