OverlayParser - butscher/WikidPad GitHub Wiki
This is a demonstration plugin for constructing a wiki language parser based on the default parser which doesn't need permanent updates (but maybe from time to time).
Until now it was only possible to create a copy of the default parser plugin and modify it in the desired way. This meaned that each change in the default parser had to be done to the copied parser as well.
For simple changes (like double brackets for links as this plugin demonstrates) it can be done more comfortable now by giving some redefinitions of definitions in default parser which are used instead of the original ones. If other parts of the default parser change an overlay parser automatically uses these changes without a manual update.
The !OverlayParser works by executing the default parser code in some sort of "guarded" environment. When one of the overlaid identifiers should be set as global variable, the !OverlayParser evaluates the definition from its PAYLOAD and sets this result instead. This allows to redefine e.g. the URL patterns to support more URL protocols.
The plugin works for recent 2.2 and 2.1 versions. For final 2.0 it should work but wasn't tested yet.
This plugin is mainly intended for demonstration purposes. If you want your own, you should rename it and modify WIKI_LANGUAGE_NAME (internal name of new wiki language) and WIKI_HR_LANGUAGE_NAME (human readable name) and of course PAYLOAD.
import re, traceback WIKIDPAD_PLUGIN = (("WikiParser", 1),) WIKI_LANGUAGE_NAME = "wikidpad_overlaid_2_0" WIKI_HR_LANGUAGE_NAME = u"WikidPad overlaid 2.0" def describeWikiLanguage(ver, app): """ API function for "WikiParser" plugins Returns a sequence of tuples describing the supported insertion keys. Each tuple has the form (intLanguageName, hrLanguageName, parserFactory, parserIsThreadsafe, editHelperFactory, editHelperIsThreadsafe) Where the items mean: intLanguageName -- internal unique name (should be ascii only) to identify wiki language processed by parser hrLanguageName -- human readable language name, unistring (TODO: localization) parserFactory -- factory function to create parser object(s) fulfilling parserIsThreadsafe -- boolean if parser is threadsafe. If not this will currently lead to a very inefficient operation processHelperFactory -- factory for helper object containing further functions needed for editing, tree presentation and so on. editHelperIsThreadsafe -- boolean if edit helper functions are threadsafe. Parameters: ver -- API version (can only be 1 currently) app -- wxApp object """ return ((WIKI_LANGUAGE_NAME, WIKI_HR_LANGUAGE_NAME, parserFactory, True, languageHelperFactory, True),) # The PAYLOAD looks like Python code but has to use a much simpler syntax: # # <identifier to overlay> = <Python expression> # # If the expression spans multiple lines each except the last one has to end # with backslash '\' # # Comments are not allowed PAYLOAD = """ BracketStart = u"[[" BracketStartPAT = ur"\[\[" BracketEnd = u"]]" BracketEndPAT = ur"\]\]" BracketStartRevPAT = ur"\[\[" BracketEndRevPAT = ur"\]\]" """ # ---------- Modifications below this line are not recommended ---------- _LINECONT_RE = re.compile(r"(?:\r\n?|\n)\\", re.UNICODE) _LINEEND_SPLIT_RE = re.compile(r"\r\n?|\n", re.UNICODE) def _joinLineConts(text): return _LINECONT_RE.sub("", text) def _splitLines(text): return _LINEEND_SPLIT_RE.split(text) class _ReplacementDictWrapper(dict): def __init__(self, realDict, replaceExprDict): self.realDict = realDict dict.update(self, realDict) self.replaceExprDict = replaceExprDict def __setitem__(self, key, value): if self.replaceExprDict.has_key(key): value = eval(self.replaceExprDict[key][0], self) self.realDict[key] = value dict.__setitem__(self, key, value) def __delitem__(self, key): del self.realDict[key] dict.__delitem__(self, key) @staticmethod def overlayToReplaceExprDict(overlay): overlay = _joinLineConts(overlay) result = {} for line in _splitLines(overlay): sp = line.split("=", 1) if len(sp) != 2: continue result[sp[0].strip()] = (sp[1].strip(),) return result _realParserFactory = None _realLanguageHelperFactory = None def _loadWorkerModule(): global _realParserFactory, _realLanguageHelperFactory from os.path import join from imp import new_module from pwiki.StringOps import loadEntireTxtFile # Find original "WikidPadParser.py" file try: # Should work with 2.2beta03 and later import wikidpadSystemPlugins targetPath = join(wikidpadSystemPlugins.__path__[0], "wikidPadParser/WikidPadParser.py") except ImportError: # Fallback method import sys targetPath = join(sys.modules[__name__.split(".", 1)[0]].__path__[0], "../extensions/wikidPadParser/WikidPadParser.py") original = loadEntireTxtFile(targetPath) module = new_module("") glSpace = _ReplacementDictWrapper(module.__dict__, _ReplacementDictWrapper.overlayToReplaceExprDict(PAYLOAD)) exec original in glSpace _realParserFactory = module.parserFactory _realLanguageHelperFactory = module.languageHelperFactory def parserFactory(intLanguageName, debugMode): """ Builds up a parser object. If the parser is threadsafe this function is allowed to return the same object multiple times (currently it should do so for efficiency). For seldom needed parsers it is recommended to put the actual parser construction as singleton in this function to reduce startup time of WikidPad. For non-threadsafe parsers it is required to create one inside this function at each call. intLanguageName -- internal unique name (should be ascii only) to identify wiki language to process by parser """ global _realParserFactory if _realParserFactory is None: _loadWorkerModule() return _realParserFactory(intLanguageName, debugMode) def languageHelperFactory(intLanguageName, debugMode): """ Builds up a language helper object. If the object is threadsafe this function is allowed to return the same object multiple times (currently it should do so for efficiency). intLanguageName -- internal unique name (should be ascii only) to identify wiki language to process by helper """ global _realLanguageHelperFactory if _realLanguageHelperFactory is None: _loadWorkerModule() return _realLanguageHelperFactory(intLanguageName, debugMode)