summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXhmikosR <xhmikosr@users.sourceforge.net>2014-03-07 09:24:13 +0200
committerXhmikosR <xhmikosr@users.sourceforge.net>2014-03-07 09:24:40 +0200
commite7a0f350adc29d7a46bab8ff6ad501a7e6e41cec (patch)
tree6b5b25116fe1c5c15a5bef1eb2c03f904476218f
parente63f2e0dcebcc22f24764654b637a4d871b31ae5 (diff)
downloadnotepad2-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--.gitignore12
-rw-r--r--scintilla/scripts/Face.py117
-rw-r--r--scintilla/scripts/FileGenerator.py158
-rw-r--r--scintilla/scripts/GenerateCaseConvert.py126
-rw-r--r--scintilla/scripts/GenerateCharacterCategory.py36
-rw-r--r--scintilla/scripts/HFacer.py58
-rw-r--r--scintilla/scripts/LexGen.py54
-rw-r--r--scintilla/scripts/ScintillaData.py193
8 files changed, 748 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index 0500b06..5cacb2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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=" "))