# RegistryGen.py - implemented 2005 by Neil Hodgson neilh@scintilla.org

# Regenerate the SinkWorld files that regisrter each known class of a particular
# type, initially lexers and decorations.
# Should be run whenever a new lexer is added or removed.
# Requires Python 2.1 or later

import string
import sys
import os

# Automatically generated sections contain start and end comments,
# a definition line and the results.
# The results are replaced by regenerating based on the definition line.
# The definition line is a comment prefix followed by "**".
# If there is a digit after the ** then this indicates which list to use
# and the digit and next character are not part of the definition
# Backslash is used as an escape within the definition line.
# The part between \( and \) is repeated for each item in the list.
# \* is replaced by each list item. \t, and \n are tab and newline.
def CopyWithInsertion(input, commentPrefix, retainDefs, marker, *lists):
	copying = 1
	listid = 0
	output = []
	if marker:
		output.append(marker)
	for line in input.split("\n"):
		isStartGenerated = line.startswith(commentPrefix + "++Autogenerated")
		if copying and not isStartGenerated:
			output.append(line)
		if isStartGenerated:
			if retainDefs:
				output.append(line)
			copying = 0
			definition = ""
		elif not copying and line.startswith(commentPrefix + "**"):
			if retainDefs:
				output.append(line)
			definition = line[len(commentPrefix + "**"):]
			listid = 0
			if definition[0] in string.digits:
				listid = int(definition[:1])
				definition = definition[2:]
			# Hide double slashes as a control character
			definition = definition.replace("\\\\", "\001")
			# Do some normal C style transforms
			definition = definition.replace("\\n", "\n")
			definition = definition.replace("\\t", "\t")
			# Get the doubled backslashes back as single backslashes
			definition = definition.replace("\001", "\\")
			startRepeat = definition.find("\\(")
			endRepeat = definition.find("\\)")
			intro = definition[:startRepeat]
			out = ""
			if intro.endswith("\n"):
				pos = 0
			else:
				pos = len(intro)
			out += intro
			middle = definition[startRepeat+2:endRepeat]
			for i in lists[listid]:
				item = middle.replace("\\*", i)
				if pos and (pos + len(item) >= 80):
					out += "\\\n"
					pos = 0
				out += item
				pos += len(item)
				if item.endswith("\n"):
					pos = 0
			outro = definition[endRepeat+2:]
			out += outro
			output.append(out)
		elif line.startswith(commentPrefix + "--Autogenerated"):
			copying = 1
			if retainDefs:
				output.append(line)
	ret = "\n".join(output)
	ret = ret.replace(" \n", "\n")
	return ret

def UpdateFile(filename, updated):
	""" If the file is different to updated then copy updated
	into the file else leave alone so CVS and make don't treat
	it as modified. """
	try:
		infile = open(filename, "rb")
	except IOError:	# File is not there yet
		out = open(filename, "wb")
		out.write(updated)
		out.close()
		print "New", filename
		return
	original = infile.read()
	infile.close()
	if updated != original:
		os.unlink(filename)
		out = open(filename, "wb")
		out.write(updated)
		out.close()
		print "Changed", filename

def RegenerateOverLists(inpath, outpath, commentPrefix, crlf, *lists):
	try:
		infile = open(inpath, "rb")
	except IOError:
		print "Can not open", inpath
		return
	original = infile.read()
	infile.close()
	contents = original.replace("\r\n", "\n")
	marker = None
	if inpath != outpath:
		marker = "// Automatically translated by RegistryGen.py from %s." % \
			os.path.basename(inpath)
	updated = CopyWithInsertion(contents, commentPrefix,
		inpath == outpath, marker, *lists)
	if crlf:
		updated = updated.replace("\n", "\r\n")
	UpdateFile(outpath, updated)

def Generate(inpath, outpath, commentPrefix, *lists):
	RegenerateOverLists(inpath, outpath, commentPrefix, 1, *lists)

def ciCompare(a,b):
    return a.lower() < b.lower()

def FindFiles(root, prefix, extensions):
	""" Return a list of the full paths of all files under root with 
	names that start with prefix"""
	for (p,d,f) in os.walk(root):
		for n in f:
			ext = os.path.splitext(n)[1]
			if ext in extensions and n.startswith(prefix):
				yield os.path.join(p,n)

def className(p):
	return os.path.basename(p).split(".")[0]

def GenerateAll():
	root = os.path.dirname(os.getcwd())
	lexFiles = list(FindFiles(root, "Lex", [".h"])) + list(FindFiles(root, "Fold", [".h"]))
	decorationFiles = list(FindFiles(root, "Decoration", [".h"]))
	decorationFiles.sort(ciCompare)
	#~ print "\n".join([className(p) for p in lexFiles])
	#~ print
	#~ print "\n".join(lexFiles)
	#~ print
	#~ print "\n".join(decorationFiles)
	
	cxxDir = os.path.join(root, "tentacle", "cxx")
	Generate(os.path.join(cxxDir, "RegisteredLexers.ct"), 
		os.path.join(cxxDir, "RegisteredLexers.cxx"), 
		"//", 
		[className(p) for p in lexFiles])
	Generate(os.path.join(cxxDir, "RegisteredDecorations.ct"), 
		os.path.join(cxxDir, "RegisteredDecorations.cxx"), 
		"//", 
		[className(p) for p in decorationFiles])

if __name__ == "__main__":
	GenerateAll()
