1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
local M = {}
local function read_file(path)
local fd = io.open(path, "r")
if not fd then
return nil
end
local content = fd:read("*a")
fd:close()
return content
end
local function write_file(path, content)
local dir = path:match("(.*/)[^/]+$")
if dir then
vim.fn.mkdir(dir, "p")
end
local fd = io.open(path, "w")
if not fd then
error("Cannot write " .. path)
end
fd:write(content)
fd:close()
end
local function extract_attributes(tag)
local attrs = {}
for k, v in tag:gmatch('(%w+)%s*=%s*"([^"]*)"') do
attrs[k] = v
end
return attrs
end
local function parse_run_configuration(xml)
-- find <configuration ...> ... </configuration>
local cfg_tag, body = xml:match("<configuration%s+([^>]*)>(.-)</configuration>")
if not cfg_tag then
-- sometimes configuration is self-closing: <configuration ... />
cfg_tag = xml:match("<configuration%s+([^/>]+)/>")
body = ""
end
if not cfg_tag then
return nil
end
local cfg_attrs = extract_attributes(cfg_tag)
local result = { _attrs = cfg_attrs, options = {} }
-- options: <option name="..." value="..."/>
for option in body:gmatch("<option%s+([^>/]-)/>") do
local a = extract_attributes(option)
if a.name and a.value then
result.options[a.name] = a.value
end
end
return result
end
local function print_table(t, indent)
indent = indent or 0
local prefix = string.rep(" ", indent)
for key, value in pairs(t) do
if type(value) == "table" then
print(prefix .. tostring(key) .. ":")
print_table(value, indent + 1)
else
print(prefix .. tostring(key) .. " = " .. tostring(value))
end
end
end
-- Map a single parsed config to a VSCode launch configuration (as Lua table)
local function map_to_vscode(parsed)
local type = parsed._attrs.type or parsed._attrs.factoryName or ""
local vscode = {
name = parsed._attrs.name or "<undefined>",
request = "launch",
}
-- Common Application type
if type:match("[Aa]pplication") then
vscode.type = "java"
vscode.mainClass = parsed.options.MAIN_CLASS_NAME
return vscode
end
error("Unknown configuration type: " .. type)
end
-- Main: scan .idea/runConfigurations and convert
function M.convert_all(opts)
opts = opts or {}
local pattern = ".idea/runConfigurations/*.xml"
local files = vim.tbl_filter(function(p)
return p ~= ""
end, vim.fn.glob(pattern, false, true))
if #files == 0 then
error(".run not found or no xml files present")
end
local configs = {}
for _, f in ipairs(files) do
local content = read_file(f)
if not content then
vim.notify("Cannot read " .. f, vim.log.levels.WARN)
end
local parsed = parse_run_configuration(content)
if parsed then
local vs = map_to_vscode(parsed)
table.insert(configs, vs)
else
vim.notify("Skipping (unrecognized) " .. f, vim.log.levels.DEBUG)
end
end
local launch = { version = "0.2.0", configurations = configs }
local ok, j = pcall(vim.json.encode, launch)
if not ok then
error("Failed to encode launch.json")
end
write_file(".vscode/launch.json", j)
return true
end
return M
|