-- lx syntax
-- batch syntax <liqube>

local syntax = require("core.syntax")

-- windows batch files use caseless matching for symbols
-- all symbols here must be lowercase
local symbols = {
	_expansion_flags = "rluc", -- raw, lower, upper, capital

	["if"] = "keyword",
	["else"] = "keyword",
	["ElsIf"] = "keyword",
	["not"] = "keyword",
	["for"] = "keyword",
	["do"] = "keyword",
	["in"] = "keyword",
	["equ"] = "keyword",
	["neq"] = "keyword",
	["lss"] = "keyword",
	["leq"] = "keyword",
	["gtr"] = "keyword",
	["geq"] = "keyword",
	["nul"] = "keyword2",
	["con"] = "keyword2",
	["prn"] = "keyword2",
	["lpt1"] = "keyword2",
	["com1"] = "keyword2",
	["com2"] = "keyword2",
	["com3"] = "keyword2",
	["com4"] = "keyword2",
	["exist"] = "keyword",
	["defined"] = "keyword",
	["ErrorLevel"] = "keyword",
	["CmdExtVersion"] = "keyword",
	["GoTo"] = "exception",
	["call"] = "keyword",
	["verify"] = "keyword",

	-- setlocal
	["EnableExtensions"] = "keyword2",
	["DisableExtensions"] = "keyword2",
	["EnableDelayExpansion"] = "keyword2",
	["DisableDelayExpansion"] = "keyword2",

	["set"] = "function",
	["SetLocal"] = "function",
	["EndLocal"] = "function",
	["EnableDelayedExpansion"] = "function",
	["echo"] = "function",
	["type"] = "function",
	["cd"] = "function",
	["ChDir"] = "function",
	["PushD"] = "function",
	["PopD"] = "function",
	["md"] = "function",
	["MkDir"] = "function",
	["pause"] = "function",
	["choice"] = "function",
	["exit"] = "exception",
	["del"] = "function",
	["rd"] = "function",
	["RmDir"] = "function",
	["copy"] = "function",
	["move"] = "function",
	["ren"] = "function",
	["find"] = "function",
	["FindStr"] = "function",
	["sort"] = "function",
	["shift"] = "function",
	["cmd"] = "function",
	["command"] = "function",
	["ForFiles"] = "function",
	["start"] = "function",

	["format"] = "function",
	["format.com"] = "function",
}

local exeutils = {
	"arp",
	"atbroker",
	"atmadm",
	"attrib",
	"auditpol",
	"bcdboot",
	"bcdedit",
	"bdehdcfg",
	"bitsadmin",
	"bootcfg",
	"bootsect",
	"certoc",
	"certreq",
	"certutil",
	"change",
	"checknetisolation",
	"chglogon",
	"chgport",
	"chgusr",
	"chkdsk",
	"chkntfs",
	"choice",
	"cipher",
	"cleanmgr",
	"cliconfg",
	"clip",
	"cmd",
	"cmdkey",
	"cmstp",
	"colorcpl",
	"comp",
	"compact",
	"control",
	"cscript",
	"cttune",
	"curl",
	"dcomcnfg",
	"defrag",
	"diantz",
	"diskpart",
	"diskraid",
	"diskshadow",
	"dism",
	"dispdiag",
	"driverquery",
	"dxdiag",
	"esentutl",
	"eventcreate",
	"eventvwr",
	"expand",
	"extrac32",
	"fc",
	"find",
	"findstr",
	"finger",
	"fltmc",
	"fondue",
	"forfiles",
	"format",
	"fsutil",
	"ftp",
	"fxscover",
	"getmac",
	"gpresult",
	"gpupdate",
	"help",
	"hostname",
	"icacls",
	"ipconfig",
	"iscsicli",
	"iscsicpl",
	"klist",
	"ksetup",
	"ktpass",
	"label",
	"licensingdiag",
	"lodctr",
	"logman",
	"logoff",
	"lpksetup",
	"lpq",
	"lpr",
	"makecab",
	"manage-bde",
	"mdsched",
	"mobsync",
	"mount",
	"mountvol",
	"mplay32",
	"mrinfo",
	"msg",
	"msiexec",
	"msinfo32",
	"mstsc",
	"mtsc",
	"nbtstat",
	"net",
	"net1",
	"netdom",
	"netsh",
	"netstat",
	"nfi",
	"nslookup",
	"ntbackup",
	"ntdsutil",
	"odbcad32",
	"oleview",
	"openfiles",
	"pathping",
	"perfmon",
	"ping",
	"pnpuninst",
	"pnputil",
	"powercfg",
	"print",
	"printbrm",
	"prncnfg",
	"prndrvr",
	"prnjobs",
	"prnmngr",
	"prnport",
	"prnqctl",
	"pwlauncher",
	"query",
	"quser",
	"qwinsta",
	"reagentc",
	"recover",
	"reg",
	"regedit",
	"regasm",
	"regsvcs",
	"regsvr32",
	"relog",
	"repair-bde",
	"replace",
	"reset",
	"resmon",
	"robocopy",
	"route",
	"rpcping",
	"runas",
	"rwinsta",
	"sc",
	"schtasks",
	"secedit",
	"setspn",
	"setx",
	"sfc",
	"shadow",
	"shrpubw",
	"shutdown",
	"sigverif",
	"sort",
	"sxstrace",
	"sysprep",
	"systeminfo",
	"takeown",
	"taskkill",
	"tasklist",
	"tcmsetup",
	"telnet",
	"tftp",
	"timeout",
	"tracert",
	"tree",
	"tsdiscon",
	"typeperf",
	"tzutil",
	"unlodctr",
	"utilman",
	"vaultcmd",
	"verifier",
	"vssadmin",
	"waitfor",
	"wbadmin",
	"wbemtest",
	"wevtutil",
	"where",
	"whoami",
	"winsat",
	"winspool",
	"winver",
	"wmic",
	"wscript",
	"wsmprovhost",
	"wusa",
	"xcopy",
}

for _, type in ipairs(exeutils) do
	symbols[type] = "keyword2"
	symbols[type .. ".exe"] = "keyword2" -- TODO: not working because of '.' ?
end

syntax.add({
	name = "Batch File",
	shortname = "Batch",
	comment = "rem",
	extensions = { "cmd", "bat" },
	headers = "^@echo",

	patterns = {
		{ pattern = "@?echo off\n", type = "meta", caseless = true, line_start = true },
		{ pattern = "@?echo on\n", type = "meta", caseless = true, line_start = true },

		-- Batch escape character
		{ pattern = "%^", type = "literal" },

		{ pattern = "%>nul", type = "meta" },
		{ pattern = "[12]?%>nul", type = "meta" },
		{ pattern = "[12]?%>%&[12]", type = "meta" },

		-- Line continuation backslash with surrounding spaces
		{ pattern = "%s*(\\)%s*\n", type = { "meta" } },

		-- Line-start command forms
		{ pattern = "%s*cd%s+", type = "function", caseless = true, line_start = true },
		{ pattern = "%s*if%s+not%s+exist%s+", type = "keyword", caseless = true, line_start = true },
		{ pattern = "%s*if%s+not%s+", type = "keyword", caseless = true, line_start = true },
		{ pattern = "%s*if%s+exist%s+", type = "keyword", caseless = true, line_start = true },

		-- @echo text.. >nul
		{
			pattern = "%s*(@?)(echo)%s+(.-)([12]?%>nul)\n",
			type = { "operator", "function", "string", "meta" },
			caseless = true,
			line_start = true,
		},
		-- @echo text.. 2>&1
		{
			pattern = "%s*(@?)(echo)%s+(.-)([12]?%>%&[12])\n",
			type = { "operator", "function", "string", "meta" },
			caseless = true,
			line_start = true,
		},
		-- @echo text..
		{
			pattern = "%s*(@?)(echo)%s+(.-)\n",
			type = { "operator", "function", "string" },
			caseless = true,
			line_start = true,
		},
		-- @echo
		{
			pattern = "%s*(@?echo)(%.)",
			type = { "operator", "function" },
			caseless = true,
			line_start = true,
		},
		-- @call :func
		{
			pattern = "%s*(@?)(call)%s+(:.-)[%s\n]",
			type = { "operator", "function", "label" },
			caseless = true,
			line_start = true,
		},
		-- Quoted set assignments consume both quotes so the trailing quote does not reopen a string
		-- @set "key=value..."
		{
			pattern = '%s*(@?)(set)%s+(%")(.-)(=)(.-)(%")\n',
			type = { "operator", "function", "operator", "variable", "operator", "string", "operator" },
			caseless = true,
			not_preceded_by = "[%w_]",
		},
		-- @set key=value..
		-- Unquoted set assignments still color key, =, and value
		{
			pattern = '%s*(@?)(set)%s+(.-)(=)',
			type = { "operator", "function", "variable", "operator" },
			caseless = true,
			not_preceded_by = "[%w_]",
		},
		--  { pattern = "%s*(@?)(set)%s.-?(=)(.-)\n",   type = { "operator", "function", "operator", "normal" },  caseless = true, line_start = true },
		{ pattern = "rem.-\n", type = "comment", caseless = true }, -- rem comment line, rem, rem

--		{ pattern = "%f[%w][%w%-_]+%.exe%s",      type = "literal" },

		-- goto :eof (only :eof token)
		{ pattern = "goto%s+:eof%f[^%w_]", type = { "exception" }, caseless = true },

		-- End labels
--		{ pattern = "%f[%w_]goto%s+end%f[^%w]", type = "literal", caseless = true },
		{ pattern = "%f[%w_]goto%s+.-\n", type = { "control" }, caseless = true },
--		{ pattern = "%s*:end%s*", type = "literal", line_start = true, caseless = true },

		{
			pattern = ":eof%f[^%w_]",
			type = "exception",
			preceded_by = ".*[Gg][Oo][Tt][Oo]%s+",
			caseless = true,
		},
		{ pattern = "%s*:[%w%-_]+", type = "label", line_start = true }, -- :labels (line start)
		{ pattern = "%:%:.-\n", type = "comment" }, -- :: comment line
		{ pattern = "%%[%w%-_%:%~%,]+%%", type = "symbol" }, -- %variable%
		{ pattern = "%![%w%-_%:%~%,]+%!", type = "bold" }, -- !variable!
		{ pattern = "%%%%?~?[%w:]+", type = "symbol" }, -- %1, %~dpn1, %~1:2, %%i, %%~i
		{ pattern = "[!=()%>&%/\\@]", type = "operator" }, -- operators
		{ pattern = "-?%.?%d+f?", type = "number" }, -- integer numbers
		{ pattern = { '"', '"', "^" }, type = "string", delim_type = "operator" }, -- "strings"
		{ pattern = "[%a_][%w_]*", type = "normal" },
	},

	-- Outline points for batch navigation
	outline_patterns = {
		{ pattern = "%s*@?echo%s+off%f[%A]", caseless = true, line_start = true },
		{ pattern = "%s*:[%w%-_]+%s*$", line_start = true },
	},

	guard_comment = {
		guard_open = "if 0==1 (",
		guard_close = ")",
		guard_align_to_indent = true,
		allow_nested = true,
	},

	symbols = symbols
})
