#!/usr/bin/env python3 # CheckMentioned.py # Find all the symbols in scintilla/include/Scintilla.h and check if they # are mentioned in scintilla/doc/ScintillaDoc.html. # Requires Python 3.6 or later import re, string, sys srcRoot = "../.." sys.path.append(srcRoot + "/scintilla/scripts") import Face uninteresting = { "SCINTILLA_H", "SCI_START", "SCI_LEXER_START", "SCI_OPTIONAL_START", # These archaic names are #defined to the Sci_ prefixed modern equivalents. # They are not documented so they are not used in new code. "CharacterRange", "TextRange", "TextToFind", "RangeToFormat", "NotifyHeader", } incFileName = srcRoot + "/scintilla/include/Scintilla.h" docFileName = srcRoot + "/scintilla/doc/ScintillaDoc.html" identCharacters = "_" + string.ascii_letters + string.digits # Convert all punctuation characters except '_' into spaces. def depunctuate(s): d = "" for ch in s: if ch in identCharacters: d = d + ch else: d = d + " " return d symbols = {} with open(incFileName, "rt") as incFile: for line in incFile.readlines(): if line.startswith("#define"): identifier = line.split()[1] symbols[identifier] = 0 with open(docFileName, "rt") as docFile: for line in docFile.readlines(): for word in depunctuate(line).split(): if word in symbols.keys(): symbols[word] = 1 def convertIFaceTypeToC(t): if t == "keymod": return "int " elif t == "string": return "const char *" elif t == "stringresult": return "char *" elif t == "cells": return "cell *" elif t == "textrange": return "Sci_TextRange *" elif t == "findtext": return "Sci_TextToFind *" elif t == "formatrange": return "Sci_RangeToFormat *" elif t == "textrangefull": return "Sci_TextRangeFull *" elif t == "findtextfull": return "Sci_TextToFindFull *" elif t == "formatrangefull": return "Sci_RangeToFormatFull *" elif Face.IsEnumeration(t): return "int " return t + " " def makeParm(t, n, v): return (convertIFaceTypeToC(t) + n).rstrip() def makeSig(params): p1 = makeParm(params["Param1Type"], params["Param1Name"], params["Param1Value"]) p2 = makeParm(params["Param2Type"], params["Param2Name"], params["Param2Value"]) retType = params["ReturnType"] if retType in ["void", "string", "stringresult"]: retType = "" elif Face.IsEnumeration(retType): retType = "int" if retType: retType = " → " + retType if p1 == "" and p2 == "": return retType if p1 == "": p1 = "<unused>" joiner = "" if p2 != "": joiner = ", " return "(" + p1 + joiner + p2 + ")" + retType pathIface = srcRoot + "/scintilla/include/Scintilla.iface" def retrieveFeatures(): face = Face.Face() face.ReadFromFile(pathIface) sciToFeature = {} sccToValue = { "true":"1", "false":"0", "EN_SETFOCUS":"256", "EN_KILLFOCUS":"512"} for name in face.order: v = face.features[name] if v["FeatureType"] in ["fun", "get", "set"]: featureDefineName = "SCI_" + name.upper() sciToFeature[featureDefineName] = name elif v["FeatureType"] in ["val"]: featureDefineName = name.upper() sccToValue[featureDefineName] = v["Value"] elif v["FeatureType"] in ["evt"]: featureDefineName = "SCN_" + name.upper() sccToValue[featureDefineName] = v["Value"] return (face, sciToFeature, sccToValue) def flattenSpaces(s): return s.replace("\n", " ").replace(" ", " ").replace(" ", " ").replace(" ", " ").strip() def printCtag(ident, path): print(ident.strip() + "\t" + path + "\t" + "/^" + ident + "$/") showCTags = True def checkDocumentation(): with open(docFileName, "rt") as docFile: docs = docFile.read() face, sciToFeature, sccToValue = retrieveFeatures() headers = {} definitions = {} # Examine header sections which point to definitions #SCI_SETLAYOUTCACHE(int cacheMode)
dirPattern = re.compile(r'([A-Z][A-Za-z0-9_() *&;,\n]+)') for api, sig in re.findall(dirPattern, docs): sigApi = re.split('\W+', sig)[0] sigFlat = flattenSpaces(sig) sigFlat = sigFlat.replace('colouralpha ', 'xxxx ') # Temporary to avoid next line sigFlat = sigFlat.replace('alpha ', 'int ') sigFlat = sigFlat.replace('xxxx ', 'colouralpha ') sigFlat = sigFlat.replace("document *", "int ") sigFlat = sigFlat.rstrip() if '(' in sigFlat or api.startswith("SCI_"): name = sciToFeature[api] sigFromFace = api + makeSig(face.features[name]) if sigFlat != sigFromFace: print(sigFlat, "|", sigFromFace) if showCTags: printCtag(api, docFileName) #~ printCtag(" " + name, pathIface) if api != sigApi: print(sigApi, ";;", sig, ";;", api) headers[api] = 1 # Warns for most keyboard commands so not enabled #~ for api in sorted(sciToFeature.keys()): #~ if api not in headers: #~ print("No header for ", api) # Examine definitions #SCI_SETLAYOUTCACHE(int cacheMode) defPattern = re.compile(r'([A-Z][A-Za-z0-9_() *#\"=<>/&;,\n-]+?)') for api, sig in re.findall(defPattern, docs): sigFlat = flattenSpaces(sig) if '(.+)', '\\1', sigFlat) sigFlat = sigFlat.replace('colouralpha ', 'xxxx ') # Temporary to avoid next line sigFlat = sigFlat.replace('alpha ', 'int ') sigFlat = sigFlat.replace('xxxx ', 'colouralpha ') sigFlat = sigFlat.replace("document *", "int ") sigFlat = sigFlat.replace(' NUL-terminated', '') sigFlat = sigFlat.rstrip() #~ sigFlat = sigFlat.replace(' NUL-terminated', '') sigApi = re.split('\W+', sigFlat)[0] #~ print(sigFlat, ";;", sig, ";;", api) if '(' in sigFlat or api.startswith("SCI_"): try: name = sciToFeature[api] sigFromFace = api + makeSig(face.features[name]) if sigFlat != sigFromFace: print(sigFlat, "|", sigFromFace) if showCTags: printCtag('="' + api, docFileName) #~ printCtag(" " + name, pathIface) except KeyError: pass # Feature removed but still has documentation if api != sigApi: print(sigApi, ";;", sig, ";;", api) definitions[api] = 1 # Warns for most keyboard commands so not enabled #~ for api in sorted(sciToFeature.keys()): #~ if api not in definitions: #~ print("No definition for ", api) outName = docFileName.replace("Doc", "Dox") with open(outName, "wt") as docFile: docFile.write(docs) # Examine constant definitions #SC_CARETSTICKY_WHITESPACE (2) constPattern = re.compile(r'(\w+) *\((\w+)\)') for name, val in re.findall(constPattern, docs): try: valOfName = sccToValue[name] if val != valOfName: print(val, "<-", name, ";;", valOfName) except KeyError: print("***", val, "<-", name) # Examine 'seealso' definitions #SCI_CREATEDOCUMENT seealsoPattern = re.compile(r'"seealso" href="#(\w+)">(\w+)[<(]') for ref, text in re.findall(seealsoPattern, docs): if ref != text: print(f"seealso {text} -> {ref}") for name in sccToValue.keys(): if name not in ["SCI_OPTIONAL_START", "SCI_LEXER_START"] and name not in docs: print(f"Unknown {name}") for identifier in sorted(symbols.keys()): if not symbols[identifier] and identifier not in uninteresting: print(identifier) checkDocumentation()