summaryrefslogtreecommitdiff
path: root/lua/intellij_to_vscode/converter.lua
blob: 1b5572f8cd7f7521b71cf2162a86f540db871458 (plain)
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