#!/usr/bin/env python3 # WidgetGen.py - regenerate the ScintillaWidgetCpp.cpp and ScintillaWidgetCpp.h files # Check that API includes all gtkscintilla2 functions import sys import os import getopt scintillaDirectory = "../.." scintillaScriptsDirectory = os.path.join(scintillaDirectory, "scripts") sys.path.append(scintillaScriptsDirectory) import Face from FileGenerator import GenerateFile def underscoreName(s): # Name conversion fixes to match gtkscintilla2 irregular = ['WS', 'EOL', 'AutoC', 'KeyWords', 'BackSpace', 'UnIndents', 'RE', 'RGBA'] for word in irregular: replacement = word[0] + word[1:].lower() s = s.replace(word, replacement) out = "" for c in s: if c.isupper(): if out: out += "_" out += c.lower() else: out += c return out def normalisedName(s, options, role=None): if options["qtStyle"]: if role == "get": s = s.replace("Get", "") return s[0].lower() + s[1:] else: return underscoreName(s) typeAliases = { "position": "int", "line": "int", "pointer": "int", "colour": "int", "colouralpha": "int", "keymod": "int", "string": "const char *", "stringresult": "const char *", "cells": "const char *", } def cppAlias(s): if s in typeAliases: return typeAliases[s] elif Face.IsEnumeration(s): return "int" else: return s understoodTypes = ["", "void", "int", "bool", "position", "line", "pointer", "colour", "colouralpha", "keymod", "string", "stringresult", "cells"] def understoodType(t): return t in understoodTypes or Face.IsEnumeration(t) def checkTypes(name, v): understandAllTypes = True if not understoodType(v["ReturnType"]): #~ print("Do not understand", v["ReturnType"], "for", name) understandAllTypes = False if not understoodType(v["Param1Type"]): #~ print("Do not understand", v["Param1Type"], "for", name) understandAllTypes = False if not understoodType(v["Param2Type"]): #~ print("Do not understand", v["Param2Type"], "for", name) understandAllTypes = False return understandAllTypes def arguments(v, stringResult, options): ret = "" p1Type = cppAlias(v["Param1Type"]) if p1Type == "int": p1Type = "sptr_t" if p1Type: ret = ret + p1Type + " " + normalisedName(v["Param1Name"], options) p2Type = cppAlias(v["Param2Type"]) if p2Type == "int": p2Type = "sptr_t" if p2Type and not stringResult: if p1Type: ret = ret + ", " ret = ret + p2Type + " " + normalisedName(v["Param2Name"], options) return ret def printPyFile(f, options): out = [] for name in f.order: v = f.features[name] if v["Category"] != "Deprecated": feat = v["FeatureType"] if feat in ["val"]: out.append(name + "=" + v["Value"]) if feat in ["evt"]: out.append("SCN_" + name.upper() + "=" + v["Value"]) if feat in ["fun"]: out.append("SCI_" + name.upper() + "=" + v["Value"]) return out def printHFile(f, options): out = [] for name in f.order: v = f.features[name] if v["Category"] != "Deprecated": feat = v["FeatureType"] if feat in ["fun", "get", "set"]: if checkTypes(name, v): constDeclarator = " const" if feat == "get" else "" returnType = cppAlias(v["ReturnType"]) if returnType == "int": returnType = "sptr_t" stringResult = v["Param2Type"] == "stringresult" if stringResult: returnType = "QByteArray" out.append("\t" + returnType + " " + normalisedName(name, options, feat) + "(" + arguments(v, stringResult, options)+ ")" + constDeclarator + ";") return out def methodNames(f, options): for name in f.order: v = f.features[name] if v["Category"] != "Deprecated": feat = v["FeatureType"] if feat in ["fun", "get", "set"]: if checkTypes(name, v): yield normalisedName(name, options) def printCPPFile(f, options): out = [] for name in f.order: v = f.features[name] if v["Category"] != "Deprecated": feat = v["FeatureType"] if feat in ["fun", "get", "set"]: if checkTypes(name, v): constDeclarator = " const" if feat == "get" else "" featureDefineName = "SCI_" + name.upper() returnType = cppAlias(v["ReturnType"]) if returnType == "int": returnType = "sptr_t" stringResult = v["Param2Type"] == "stringresult" if stringResult: returnType = "QByteArray" returnStatement = "" if returnType != "void": returnStatement = "return " out.append(returnType + " ScintillaEdit::" + normalisedName(name, options, feat) + "(" + arguments(v, stringResult, options) + ")" + constDeclarator + " {") returns = "" if stringResult: returns += " " + returnStatement + "TextReturner(" + featureDefineName + ", " if "*" in cppAlias(v["Param1Type"]): returns += "(sptr_t)" if v["Param1Name"]: returns += normalisedName(v["Param1Name"], options) else: returns += "0" returns += ");" else: returns += " " + returnStatement + "send(" + featureDefineName + ", " if "*" in cppAlias(v["Param1Type"]): returns += "(sptr_t)" if v["Param1Name"]: returns += normalisedName(v["Param1Name"], options) else: returns += "0" returns += ", " if "*" in cppAlias(v["Param2Type"]): returns += "(sptr_t)" if v["Param2Name"]: returns += normalisedName(v["Param2Name"], options) else: returns += "0" returns += ");" out.append(returns) out.append("}") out.append("") return out def gtkNames(): # The full path on my machine: should be altered for anyone else p = "C:/Users/Neil/Downloads/wingide-source-4.0.1-1/wingide-source-4.0.1-1/external/gtkscintilla2/gtkscintilla.c" with open(p) as f: for l in f.readlines(): if "gtk_scintilla_" in l: name = l.split()[1][14:] if '(' in name: name = name.split('(')[0] yield name def usage(): print("WidgetGen.py [-c|--clean][-h|--help][-u|--underscore-names]") print("") print("Generate full APIs for ScintillaEdit class and ScintillaConstants.py.") print("") print("options:") print("") print("-c --clean remove all generated code from files") print("-h --help display this text") print("-u --underscore-names use method_names consistent with GTK+ standards") def readInterface(cleanGenerated): f = Face.Face() if not cleanGenerated: f.ReadFromFile("../../include/Scintilla.iface") return f def main(argv): # Using local path for gtkscintilla2 so don't default to checking checkGTK = False cleanGenerated = False qtStyleInterface = True # The --gtk-check option checks for full coverage of the gtkscintilla2 API but # depends on a particular directory so is not mentioned in --help. opts, args = getopt.getopt(argv, "hcgu", ["help", "clean", "gtk-check", "underscore-names"]) for opt, arg in opts: if opt in ("-h", "--help"): usage() sys.exit() elif opt in ("-c", "--clean"): cleanGenerated = True elif opt in ("-g", "--gtk-check"): checkGTK = True elif opt in ("-u", "--underscore-names"): qtStyleInterface = False options = {"qtStyle": qtStyleInterface} f = readInterface(cleanGenerated) try: GenerateFile("ScintillaEdit.cpp.template", "ScintillaEdit.cpp", "/* ", True, printCPPFile(f, options)) GenerateFile("ScintillaEdit.h.template", "ScintillaEdit.h", "/* ", True, printHFile(f, options)) GenerateFile("../ScintillaEditPy/ScintillaConstants.py.template", "../ScintillaEditPy/ScintillaConstants.py", "# ", True, printPyFile(f, options)) if checkGTK: names = set(methodNames(f)) #~ print("\n".join(names)) namesGtk = set(gtkNames()) for name in namesGtk: if name not in names: print(name, "not found in Qt version") for name in names: if name not in namesGtk: print(name, "not found in GTK+ version") except: raise if cleanGenerated: for file in ["ScintillaEdit.cpp", "ScintillaEdit.h", "../ScintillaEditPy/ScintillaConstants.py"]: try: os.remove(file) except OSError: pass if __name__ == "__main__": main(sys.argv[1:])