diff options
author | XhmikosR <xhmikosr@users.sourceforge.net> | 2014-03-07 09:24:13 +0200 |
---|---|---|
committer | XhmikosR <xhmikosr@users.sourceforge.net> | 2014-03-07 09:24:40 +0200 |
commit | e7a0f350adc29d7a46bab8ff6ad501a7e6e41cec (patch) | |
tree | 6b5b25116fe1c5c15a5bef1eb2c03f904476218f | |
parent | e63f2e0dcebcc22f24764654b637a4d871b31ae5 (diff) | |
download | notepad2-mod-e7a0f350adc29d7a46bab8ff6ad501a7e6e41cec.zip notepad2-mod-e7a0f350adc29d7a46bab8ff6ad501a7e6e41cec.tar.gz notepad2-mod-e7a0f350adc29d7a46bab8ff6ad501a7e6e41cec.tar.bz2 |
Add scintilla scripts folder back.
LexGen.py has a couple of stuff commented out since we don't use them.
Close #74.
-rw-r--r-- | .gitignore | 12 | ||||
-rw-r--r-- | scintilla/scripts/Face.py | 117 | ||||
-rw-r--r-- | scintilla/scripts/FileGenerator.py | 158 | ||||
-rw-r--r-- | scintilla/scripts/GenerateCaseConvert.py | 126 | ||||
-rw-r--r-- | scintilla/scripts/GenerateCharacterCategory.py | 36 | ||||
-rw-r--r-- | scintilla/scripts/HFacer.py | 58 | ||||
-rw-r--r-- | scintilla/scripts/LexGen.py | 54 | ||||
-rw-r--r-- | scintilla/scripts/ScintillaData.py | 193 |
8 files changed, 748 insertions, 6 deletions
@@ -1,8 +1,11 @@ +*.aps +*.opensdf +*.pyc +*.sdf +*.suo +*.user /bin /build.user.bat -/build/*.opensdf -/build/*.sdf -/build/*.suo /build/cov-int /build/Notepad2-mod.tar /build/Notepad2-mod.tgz @@ -10,8 +13,5 @@ /build/temp_zip* /distrib/Notepad2-mod.*.exe /res/Notepad2.exe.manifest -/scintilla/*.user /signinfo_notepad2-mod.txt -/src/*.aps -/src/*.user /src/VersionRev.h diff --git a/scintilla/scripts/Face.py b/scintilla/scripts/Face.py new file mode 100644 index 0000000..17d161f --- /dev/null +++ b/scintilla/scripts/Face.py @@ -0,0 +1,117 @@ +# Face.py - module for reading and parsing Scintilla.iface file +# Implemented 2000 by Neil Hodgson neilh@scintilla.org +# Released to the public domain. +# Requires Python 2.5 or later + +def sanitiseLine(line): + if line[-1:] == '\n': line = line[:-1] + if line.find("##") != -1: + line = line[:line.find("##")] + line = line.strip() + return line + +def decodeFunction(featureVal): + retType, rest = featureVal.split(" ", 1) + nameIdent, params = rest.split("(") + name, value = nameIdent.split("=") + params, rest = params.split(")") + param1, param2 = params.split(",") + return retType, name, value, param1, param2 + +def decodeEvent(featureVal): + retType, rest = featureVal.split(" ", 1) + nameIdent, params = rest.split("(") + name, value = nameIdent.split("=") + return retType, name, value + +def decodeParam(p): + param = p.strip() + type = "" + name = "" + value = "" + if " " in param: + type, nv = param.split(" ") + if "=" in nv: + name, value = nv.split("=") + else: + name = nv + return type, name, value + +class Face: + + def __init__(self): + self.order = [] + self.features = {} + self.values = {} + self.events = {} + + def ReadFromFile(self, name): + currentCategory = "" + currentComment = [] + currentCommentFinished = 0 + file = open(name) + for line in file.readlines(): + line = sanitiseLine(line) + if line: + if line[0] == "#": + if line[1] == " ": + if currentCommentFinished: + currentComment = [] + currentCommentFinished = 0 + currentComment.append(line[2:]) + else: + currentCommentFinished = 1 + featureType, featureVal = line.split(" ", 1) + if featureType in ["fun", "get", "set"]: + try: + retType, name, value, param1, param2 = decodeFunction(featureVal) + except ValueError: + print("Failed to decode %s" % line) + raise + p1 = decodeParam(param1) + p2 = decodeParam(param2) + self.features[name] = { + "FeatureType": featureType, + "ReturnType": retType, + "Value": value, + "Param1Type": p1[0], "Param1Name": p1[1], "Param1Value": p1[2], + "Param2Type": p2[0], "Param2Name": p2[1], "Param2Value": p2[2], + "Category": currentCategory, "Comment": currentComment + } + if value in self.values: + raise Exception("Duplicate value " + value + " " + name) + self.values[value] = 1 + self.order.append(name) + elif featureType == "evt": + retType, name, value = decodeEvent(featureVal) + self.features[name] = { + "FeatureType": featureType, + "ReturnType": retType, + "Value": value, + "Category": currentCategory, "Comment": currentComment + } + if value in self.events: + raise Exception("Duplicate event " + value + " " + name) + self.events[value] = 1 + self.order.append(name) + elif featureType == "cat": + currentCategory = featureVal + elif featureType == "val": + try: + name, value = featureVal.split("=", 1) + except ValueError: + print("Failure %s" % featureVal) + raise Exception() + self.features[name] = { + "FeatureType": featureType, + "Category": currentCategory, + "Value": value } + self.order.append(name) + elif featureType == "enu" or featureType == "lex": + name, value = featureVal.split("=", 1) + self.features[name] = { + "FeatureType": featureType, + "Category": currentCategory, + "Value": value } + self.order.append(name) + diff --git a/scintilla/scripts/FileGenerator.py b/scintilla/scripts/FileGenerator.py new file mode 100644 index 0000000..01a79bf --- /dev/null +++ b/scintilla/scripts/FileGenerator.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +# FileGenerator.py - implemented 2013 by Neil Hodgson neilh@scintilla.org +# Released to the public domain. + +# Generate or regenerate source files based on comments in those files. +# May be modified in-place or a template may be generated into a complete file. +# Requires Python 2.5 or later +# The files are copied to a string apart from sections between a +# ++Autogenerated comment and a --Autogenerated comment which is +# generated by the CopyWithInsertion function. After the whole string is +# instantiated, it is compared with the target file and if different the file +# is rewritten. + +from __future__ import with_statement + +import codecs, os, re, string, sys + +lineEnd = "\r\n" if sys.platform == "win32" else "\n" + +def UpdateFile(filename, updated): + """ If the file contents are different to updated then copy updated into the + file else leave alone so Mercurial and make don't treat it as modified. """ + newOrChanged = "Changed" + try: + with codecs.open(filename, "r", "utf-8") as infile: + original = infile.read() + if updated == original: + # Same as before so don't write + return + os.unlink(filename) + except IOError: # File is not there yet + newOrChanged = "New" + with codecs.open(filename, "w", "utf-8") as outfile: + outfile.write(updated) + print("%s %s" % (newOrChanged, filename)) + +# 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. +# If there is no definition line than the first list is copied verbatim. +# If retainDefs then the comments controlling generation are copied. +def CopyWithInsertion(input, commentPrefix, retainDefs, lists): + copying = 1 + generated = False + listid = 0 + output = [] + for line in input.splitlines(0): + isStartGenerated = line.lstrip().startswith(commentPrefix + "++Autogenerated") + if copying and not isStartGenerated: + output.append(line) + if isStartGenerated: + if retainDefs: + output.append(line) + copying = 0 + generated = False + elif not copying and not generated: + # Generating + if line.startswith(commentPrefix + "**"): + # Pattern to transform input data + if retainDefs: + output.append(line) + definition = line[len(commentPrefix + "**"):] + if (commentPrefix == "<!--") and (" -->" in definition): + definition = definition.replace(" -->", "") + 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 + out = out.replace("\n", lineEnd) # correct EOLs in generated content + output.append(out) + else: + # Simple form with no rule to transform input + output.extend(lists[0]) + generated = True + if line.lstrip().startswith(commentPrefix + "--Autogenerated") or \ + line.lstrip().startswith(commentPrefix + "~~Autogenerated"): + copying = 1 + if retainDefs: + output.append(line) + output = [line.rstrip(" \t") for line in output] # trim trailing whitespace + return lineEnd.join(output) + lineEnd + +def GenerateFile(inpath, outpath, commentPrefix, retainDefs, *lists): + """Generate 'outpath' from 'inpath'. + """ + + try: + with codecs.open(inpath, "r", "UTF-8") as infile: + original = infile.read() + updated = CopyWithInsertion(original, commentPrefix, + retainDefs, lists) + UpdateFile(outpath, updated) + except IOError: + print("Can not open %s" % inpath) + +def Generate(inpath, outpath, commentPrefix, *lists): + """Generate 'outpath' from 'inpath'. + """ + GenerateFile(inpath, outpath, commentPrefix, inpath == outpath, *lists) + +def Regenerate(filename, commentPrefix, *lists): + """Regenerate the given file. + """ + Generate(filename, filename, commentPrefix, *lists) + +def UpdateLineInFile(path, linePrefix, lineReplace): + lines = [] + updated = False + with codecs.open(path, "r", "utf-8") as f: + for l in f.readlines(): + l = l.rstrip() + if not updated and l.startswith(linePrefix): + lines.append(lineReplace) + updated = True + else: + lines.append(l) + contents = lineEnd.join(lines) + lineEnd + UpdateFile(path, contents) + +def ReplaceREInFile(path, match, replace): + with codecs.open(path, "r", "utf-8") as f: + contents = f.read() + contents = re.sub(match, replace, contents) + UpdateFile(path, contents) diff --git a/scintilla/scripts/GenerateCaseConvert.py b/scintilla/scripts/GenerateCaseConvert.py new file mode 100644 index 0000000..37506b7 --- /dev/null +++ b/scintilla/scripts/GenerateCaseConvert.py @@ -0,0 +1,126 @@ +# Script to generate CaseConvert.cxx from Python's Unicode data +# Should be run rarely when a Python with a new version of Unicode data is available. +# Requires Python 3.3 or later +# Should not be run with old versions of Python. + +# Current best approach divides case conversions into two cases: +# simple symmetric and complex. +# Simple symmetric is where a lower and upper case pair convert to each +# other and the folded form is the same as the lower case. +# There are 1006 symmetric pairs. +# These are further divided into ranges (stored as lower, upper, range length, +# range pitch and singletons (stored as lower, upper). +# Complex is for cases that don't fit the above: where there are multiple +# characters in one of the forms or fold is different to lower or +# lower(upper(x)) or upper(lower(x)) are not x. These are represented as UTF-8 +# strings with original, folded, upper, and lower separated by '|'. +# There are 126 complex cases. + +import codecs, itertools, os, string, sys, unicodedata + +from FileGenerator import Regenerate + +def contiguousRanges(l, diff): + # l is s list of lists + # group into lists where first element of each element differs by diff + out = [[l[0]]] + for s in l[1:]: + if s[0] != out[-1][-1][0] + diff: + out.append([]) + out[-1].append(s) + return out + +def flatten(listOfLists): + "Flatten one level of nesting" + return itertools.chain.from_iterable(listOfLists) + +def conversionSets(): + # For all Unicode characters, see whether they have case conversions + # Return 2 sets: one of simple symmetric conversion cases and another + # with complex cases. + complexes = [] + symmetrics = [] + for ch in range(sys.maxunicode): + if ch >= 0xd800 and ch <= 0xDBFF: + continue + if ch >= 0xdc00 and ch <= 0xDFFF: + continue + uch = chr(ch) + + fold = uch.casefold() + upper = uch.upper() + lower = uch.lower() + symmetric = False + if uch != upper and len(upper) == 1 and uch == lower and uch == fold: + lowerUpper = upper.lower() + foldUpper = upper.casefold() + if lowerUpper == foldUpper and lowerUpper == uch: + symmetric = True + symmetrics.append((ch, ord(upper), ch - ord(upper))) + if uch != lower and len(lower) == 1 and uch == upper and lower == fold: + upperLower = lower.upper() + if upperLower == uch: + symmetric = True + + if fold == uch: + fold = "" + if upper == uch: + upper = "" + if lower == uch: + lower = "" + + if (fold or upper or lower) and not symmetric: + complexes.append((uch, fold, upper, lower)) + + return symmetrics, complexes + +def groupRanges(symmetrics): + # Group the symmetrics into groups where possible, returning a list + # of ranges and a list of symmetrics that didn't fit into a range + + def distance(s): + return s[2] + + groups = [] + uniquekeys = [] + for k, g in itertools.groupby(symmetrics, distance): + groups.append(list(g)) # Store group iterator as a list + uniquekeys.append(k) + + contiguousGroups = flatten([contiguousRanges(g, 1) for g in groups]) + longGroups = [(x[0][0], x[0][1], len(x), 1) for x in contiguousGroups if len(x) > 4] + + oneDiffs = [s for s in symmetrics if s[2] == 1] + contiguousOnes = flatten([contiguousRanges(g, 2) for g in [oneDiffs]]) + longOneGroups = [(x[0][0], x[0][1], len(x), 2) for x in contiguousOnes if len(x) > 4] + + rangeGroups = sorted(longGroups+longOneGroups, key=lambda s: s[0]) + + rangeCoverage = list(flatten([range(r[0], r[0]+r[2]*r[3], r[3]) for r in rangeGroups])) + + nonRanges = [(l, u) for l, u, d in symmetrics if l not in rangeCoverage] + + return rangeGroups, nonRanges + +def escape(s): + return "".join((chr(c) if chr(c) in string.ascii_letters else "\\x%x" % c) for c in s.encode('utf-8')) + +def updateCaseConvert(): + symmetrics, complexes = conversionSets() + + rangeGroups, nonRanges = groupRanges(symmetrics) + + print(len(rangeGroups), "ranges") + rangeLines = ["%d,%d,%d,%d, " % x for x in rangeGroups] + + print(len(nonRanges), "non ranges") + nonRangeLines = ["%d,%d, " % x for x in nonRanges] + + print(len(symmetrics), "symmetric") + + complexLines = ['"%s|%s|%s|%s|"' % tuple(escape(t) for t in x) for x in complexes] + print(len(complexLines), "complex") + + Regenerate("../src/CaseConvert.cxx", "//", rangeLines, nonRangeLines, complexLines) + +updateCaseConvert() diff --git a/scintilla/scripts/GenerateCharacterCategory.py b/scintilla/scripts/GenerateCharacterCategory.py new file mode 100644 index 0000000..4596eec --- /dev/null +++ b/scintilla/scripts/GenerateCharacterCategory.py @@ -0,0 +1,36 @@ +# Script to generate CharacterCategory.cxx from Python's Unicode data +# Should be run rarely when a Python with a new version of Unicode data is available. +# Requires Python 3.3 or later +# Should not be run with old versions of Python. + +import codecs, os, platform, sys, unicodedata + +from FileGenerator import Regenerate + +def findCategories(filename): + with codecs.open(filename, "r", "UTF-8") as infile: + lines = [x.strip() for x in infile.readlines() if "\tcc" in x] + values = "".join(lines).replace(" ","").split(",") + print(values) + return [v[2:] for v in values] + +def updateCharacterCategory(filename): + values = ["// Created with Python %s, Unicode %s" % ( + platform.python_version(), unicodedata.unidata_version)] + category = unicodedata.category(chr(0)) + startRange = 0 + for ch in range(sys.maxunicode): + uch = chr(ch) + if unicodedata.category(uch) != category: + value = startRange * 32 + categories.index(category) + values.append("%d," % value) + category = unicodedata.category(uch) + startRange = ch + value = startRange * 32 + categories.index(category) + values.append("%d," % value) + + Regenerate(filename, "//", values) + +categories = findCategories("../lexlib/CharacterCategory.h") + +updateCharacterCategory("../lexlib/CharacterCategory.cxx") diff --git a/scintilla/scripts/HFacer.py b/scintilla/scripts/HFacer.py new file mode 100644 index 0000000..819181f --- /dev/null +++ b/scintilla/scripts/HFacer.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# HFacer.py - regenerate the Scintilla.h and SciLexer.h files from the Scintilla.iface interface +# definition file. +# Implemented 2000 by Neil Hodgson neilh@scintilla.org +# Requires Python 2.5 or later + +import sys +import os +import Face + +from FileGenerator import UpdateFile, Generate, Regenerate, UpdateLineInFile, lineEnd + +def printLexHFile(f): + out = [] + for name in f.order: + v = f.features[name] + if v["FeatureType"] in ["val"]: + if "SCE_" in name or "SCLEX_" in name: + out.append("#define " + name + " " + v["Value"]) + return out + +def printHFile(f): + out = [] + previousCategory = "" + for name in f.order: + v = f.features[name] + if v["Category"] != "Deprecated": + if v["Category"] == "Provisional" and previousCategory != "Provisional": + out.append("#ifndef SCI_DISABLE_PROVISIONAL") + previousCategory = v["Category"] + if v["FeatureType"] in ["fun", "get", "set"]: + featureDefineName = "SCI_" + name.upper() + out.append("#define " + featureDefineName + " " + v["Value"]) + elif v["FeatureType"] in ["evt"]: + featureDefineName = "SCN_" + name.upper() + out.append("#define " + featureDefineName + " " + v["Value"]) + elif v["FeatureType"] in ["val"]: + if not ("SCE_" in name or "SCLEX_" in name): + out.append("#define " + name + " " + v["Value"]) + out.append("#endif") + return out + +def RegenerateAll(root, showMaxID): + f = Face.Face() + f.ReadFromFile(root + "include/Scintilla.iface") + Regenerate(root + "include/Scintilla.h", "/* ", printHFile(f)) + Regenerate(root + "include/SciLexer.h", "/* ", printLexHFile(f)) + if showMaxID: + valueSet = set(int(x) for x in f.values if int(x) < 3000) + maximumID = max(valueSet) + print("Maximum ID is %d" % maximumID) + #~ valuesUnused = sorted(x for x in range(2001,maximumID) if x not in valueSet) + #~ print("\nUnused values") + #~ for v in valuesUnused: + #~ print(v) + +if __name__ == "__main__": + RegenerateAll("../", True) diff --git a/scintilla/scripts/LexGen.py b/scintilla/scripts/LexGen.py new file mode 100644 index 0000000..2724165 --- /dev/null +++ b/scintilla/scripts/LexGen.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# LexGen.py - implemented 2002 by Neil Hodgson neilh@scintilla.org +# Released to the public domain. + +# Regenerate the Scintilla source files that list all the lexers. +# Should be run whenever a new lexer is added or removed. +# Requires Python 2.5 or later +# Files are regenerated in place with templates stored in comments. +# The format of generation comments is documented in FileGenerator.py. + +from FileGenerator import Regenerate, UpdateLineInFile, ReplaceREInFile +import ScintillaData +import HFacer + +def UpdateVersionNumbers(sci, root): + UpdateLineInFile(root + "win32/ScintRes.rc", "#define VERSION_SCINTILLA", + "#define VERSION_SCINTILLA \"" + sci.versionDotted + "\"") + UpdateLineInFile(root + "win32/ScintRes.rc", "#define VERSION_WORDS", + "#define VERSION_WORDS " + sci.versionCommad) + UpdateLineInFile(root + "qt/ScintillaEditBase/ScintillaEditBase.pro", + "VERSION =", + "VERSION = " + sci.versionDotted) + UpdateLineInFile(root + "qt/ScintillaEdit/ScintillaEdit.pro", + "VERSION =", + "VERSION = " + sci.versionDotted) + UpdateLineInFile(root + "doc/ScintillaDownload.html", " Release", + " Release " + sci.versionDotted) + ReplaceREInFile(root + "doc/ScintillaDownload.html", + r"/scintilla/([a-zA-Z]+)\d\d\d", + r"/scintilla/\g<1>" + sci.version) + UpdateLineInFile(root + "doc/index.html", + ' <font color="#FFCC99" size="3"> Release version', + ' <font color="#FFCC99" size="3"> Release version ' +\ + sci.versionDotted + '<br />') + UpdateLineInFile(root + "doc/index.html", + ' Site last modified', + ' Site last modified ' + sci.mdyModified + '</font>') + UpdateLineInFile(root + "doc/ScintillaHistory.html", + ' Released ', + ' Released ' + sci.dmyModified + '.') + +def RegenerateAll(root): + + sci = ScintillaData.ScintillaData(root) + + Regenerate(root + "src/Catalogue.cxx", "//", sci.lexerModules) +# Regenerate(root + "win32/scintilla.mak", "#", sci.lexFiles) #commented out + +# UpdateVersionNumbers(sci, root) #commented out + +# HFacer.RegenerateAll(root, False) #commented out + +if __name__=="__main__": + RegenerateAll("../") diff --git a/scintilla/scripts/ScintillaData.py b/scintilla/scripts/ScintillaData.py new file mode 100644 index 0000000..1c999a1 --- /dev/null +++ b/scintilla/scripts/ScintillaData.py @@ -0,0 +1,193 @@ +# ScintillaData.py - implemented 2013 by Neil Hodgson neilh@scintilla.org +# Released to the public domain. + +# Common code used by Scintilla and SciTE for source file regeneration. +# The ScintillaData object exposes information about Scintilla as properties: +# Version properties +# version +# versionDotted +# versionCommad +# +# Date last modified +# dateModified +# yearModified +# mdyModified +# dmyModified +# myModified +# +# Information about lexers and properties defined in lexers +# lexFiles +# sorted list of lexer files +# lexerModules +# sorted list of module names +# lexerProperties +# sorted list of lexer properties +# propertyDocuments +# dictionary of property documentation { name: document string } + +# This file can be run to see the data it provides. +# Requires Python 2.5 or later + +from __future__ import with_statement + +import datetime, glob, os, textwrap + +import FileGenerator + +def FindModules(lexFile): + modules = [] + with open(lexFile) as f: + for l in f.readlines(): + if l.startswith("LexerModule"): + l = l.replace("(", " ") + modules.append(l.split()[1]) + return modules + +# Properties that start with lexer. or fold. are automatically found but there are some +# older properties that don't follow this pattern so must be explicitly listed. +knownIrregularProperties = [ + "fold", + "styling.within.preprocessor", + "tab.timmy.whinge.level", + "asp.default.language", + "html.tags.case.sensitive", + "ps.level", + "ps.tokenize", + "sql.backslash.escapes", + "nsis.uservars", + "nsis.ignorecase" +] + +def FindProperties(lexFile): + properties = {} + with open(lexFile) as f: + for l in f.readlines(): + if ("GetProperty" in l or "DefineProperty" in l) and "\"" in l: + l = l.strip() + if not l.startswith("//"): # Drop comments + propertyName = l.split("\"")[1] + if propertyName.lower() == propertyName: + # Only allow lower case property names + if propertyName in knownIrregularProperties or \ + propertyName.startswith("fold.") or \ + propertyName.startswith("lexer."): + properties[propertyName] = 1 + return properties + +def FindPropertyDocumentation(lexFile): + documents = {} + with open(lexFile) as f: + name = "" + for l in f.readlines(): + l = l.strip() + if "// property " in l: + propertyName = l.split()[2] + if propertyName.lower() == propertyName: + # Only allow lower case property names + name = propertyName + documents[name] = "" + elif "DefineProperty" in l and "\"" in l: + propertyName = l.split("\"")[1] + if propertyName.lower() == propertyName: + # Only allow lower case property names + name = propertyName + documents[name] = "" + elif name: + if l.startswith("//"): + if documents[name]: + documents[name] += " " + documents[name] += l[2:].strip() + elif l.startswith("\""): + l = l[1:].strip() + if l.endswith(";"): + l = l[:-1].strip() + if l.endswith(")"): + l = l[:-1].strip() + if l.endswith("\""): + l = l[:-1] + # Fix escaped double quotes + l = l.replace("\\\"", "\"") + documents[name] += l + else: + name = "" + for name in list(documents.keys()): + if documents[name] == "": + del documents[name] + return documents + +def ciCompare(a,b): + return cmp(a.lower(), b.lower()) + +def ciKey(a): + return a.lower() + +def SortListInsensitive(l): + try: # Try key function + l.sort(key=ciKey) + except TypeError: # Earlier version of Python, so use comparison function + l.sort(ciCompare) + +class ScintillaData: + def __init__(self, scintillaRoot): + # Discover verion information + with open(scintillaRoot + "version.txt") as f: + self.version = f.read().strip() + self.versionDotted = self.version[0] + '.' + self.version[1] + '.' + \ + self.version[2] + self.versionCommad = self.version[0] + ', ' + self.version[1] + ', ' + \ + self.version[2] + ', 0' + + with open(scintillaRoot + "doc/index.html") as f: + self.dateModified = [l for l in f.readlines() if "Date.Modified" in l]\ + [0].split('\"')[3] + # 20130602 + # index.html, SciTE.html + dtModified = datetime.datetime.strptime(self.dateModified, "%Y%m%d") + self.yearModified = self.dateModified[0:4] + monthModified = dtModified.strftime("%B") + dayModified = "%d" % dtModified.day + self.mdyModified = monthModified + " " + dayModified + " " + self.yearModified + # May 22 2013 + # index.html, SciTE.html + self.dmyModified = dayModified + " " + monthModified + " " + self.yearModified + # 22 May 2013 + # ScintillaHistory.html -- only first should change + self.myModified = monthModified + " " + self.yearModified + + # Find all the lexer source code files + lexFilePaths = glob.glob(scintillaRoot + "lexers/Lex*.cxx") + SortListInsensitive(lexFilePaths) + self.lexFiles = [os.path.basename(f)[:-4] for f in lexFilePaths] + self.lexerModules = [] + lexerProperties = set() + self.propertyDocuments = {} + for lexFile in lexFilePaths: + self.lexerModules.extend(FindModules(lexFile)) + for k in FindProperties(lexFile).keys(): + lexerProperties.add(k) + documents = FindPropertyDocumentation(lexFile) + for k in documents.keys(): + if k not in self.propertyDocuments: + self.propertyDocuments[k] = documents[k] + SortListInsensitive(self.lexerModules) + self.lexerProperties = list(lexerProperties) + SortListInsensitive(self.lexerProperties) + +def printWrapped(text): + print(textwrap.fill(text, subsequent_indent=" ")) + +if __name__=="__main__": + sci = ScintillaData("../") + print("Version %s %s %s" % (sci.version, sci.versionDotted, sci.versionCommad)) + print("Date last modified %s %s %s %s %s" % ( + sci.dateModified, sci.yearModified, sci.mdyModified, sci.dmyModified, sci.myModified)) + printWrapped(str(len(sci.lexFiles)) + " lexer files: " + ", ".join(sci.lexFiles)) + printWrapped(str(len(sci.lexerModules)) + " lexer modules: " + ", ".join(sci.lexerModules)) + printWrapped("Lexer properties: " + ", ".join(sci.lexerProperties)) + print("Lexer property documentation:") + documentProperties = list(sci.propertyDocuments.keys()) + SortListInsensitive(documentProperties) + for k in documentProperties: + print(" " + k) + print(textwrap.fill(sci.propertyDocuments[k], initial_indent=" ", + subsequent_indent=" ")) |