diff --git a/codewave_core/box_helper.py b/codewave_core/box_helper.py index 836632c..29b057e 100644 --- a/codewave_core/box_helper.py +++ b/codewave_core/box_helper.py @@ -5,20 +5,27 @@ class BoxHelper(): def __init__(self, context, options = {}): self.context = context - defaults = { + self.defaults = { 'deco': self.context.codewave.deco, 'pad': 2, 'width': 50, 'height': 3, 'openText': '', 'closeText': '', + 'prefix': '', + 'suffix': '', 'indent': 0 } - for key, val in defaults.items(): + for key, val in self.defaults.items(): if key in options: setattr(self,key,options[key]) else: setattr(self,key,val) + def clone(self): + opt = {} + for key, val in self.defaults.items(): + opt[key] = getattr(self,key) + return BoxHelper(self.context,opt) def draw(self,text): return self.startSep() + "\n" + self.lines(text) + "\n"+ self.endSep() def wrapComment(self,str): @@ -28,10 +35,10 @@ def separator(self): return self.wrapComment(self.decoLine(len)) def startSep(self): ln = self.width + 2 * self.pad + 2 * len(self.deco) - len(self.openText) - return self.wrapComment(self.openText+self.decoLine(ln)) + return self.prefix + self.wrapComment(self.openText+self.decoLine(ln)) def endSep(self): ln = self.width + 2 * self.pad + 2 * len(self.deco) - len(self.closeText) - return self.wrapComment(self.closeText+self.decoLine(ln)) + return self.wrapComment(self.closeText+self.decoLine(ln)) + self.suffix def decoLine(self,len): return util.repeatToLength(self.deco, len) def padding(self): @@ -53,20 +60,58 @@ def line(self,text = ''): self.padding() + self.deco )) + def left(self): + return self.context.wrapCommentLeft(self.deco + self.padding()) + def right(self): + return self.context.wrapCommentRight(self.padding() + self.deco) def removeIgnoredContent(self,text): return self.context.codewave.removeMarkers(self.context.codewave.removeCarret(text)) def textBounds(self,text): return util.getTxtSize(self.removeIgnoredContent(text)) def getBoxForPos(self,pos): - startFind = self.context.wrapCommentLeft(self.deco + self.deco) - endFind = self.context.wrapCommentRight(self.deco + self.deco) - start = self.context.codewave.findPrev(pos.start, startFind) - end = self.context.codewave.findNext(pos.end, endFind) - if start is not None and end is not None: - return util.Pos(start,end + len(endFind)) + depth = self.getNestedLvl(pos.start) + if depth > 0: + left = self.left() + curLeft = util.repeat(left,depth-1) + + clone = self.clone() + placeholder = "###PlaceHolder###" + clone.width = len(placeholder) + clone.openText = clone.closeText = self.deco + self.deco + placeholder + self.deco + self.deco + escPlaceholder = util.escapeRegExp("###PlaceHolder###") + + + startFind = re.compile(util.escapeRegExp(curLeft + clone.startSep()).replace(escPlaceholder,'.*')) + endFind = re.compile(util.escapeRegExp(curLeft + clone.endSep()).replace(escPlaceholder,'.*')) + + + pair = util.Pair(startFind,endFind,{ + 'validMatch': self.validPairMatch + }) + res = pair.wrapperPos(pos,self.context.codewave.editor.text) + + if res is not None: + res.start += len(curLeft) + return res + + def validPairMatch(self,match): + left = self.left() + f = self.context.codewave.findAnyNext(match.start() ,[left,"\n","\r"],-1) + return not f is not None or f.str != left + + def getNestedLvl(self,index): + depth = 0 + left = self.left() + while True: + f = self.context.codewave.findAnyNext(index ,[left,"\n","\r"],-1) + if f is None or f.str != left: + break + index = f.pos + depth+=1 + return depth def getOptFromLine(self,line,getPad=True): rStart = re.compile("(\\s*)("+util.escapeRegExp(self.context.wrapCommentLeft(self.deco))+")(\\s*)") - rEnd = re.compile("(\\s*)("+util.escapeRegExp(self.context.wrapCommentRight(self.deco))+")") + rEnd = re.compile("(\\s*)("+util.escapeRegExp(self.context.wrapCommentRight(self.deco))+")(\n|$)") resStart = rStart.search(line) resEnd = rEnd.search(line) if resStart is not None and resEnd is not None: diff --git a/codewave_core/closing_promp.py b/codewave_core/closing_promp.py index ba3d331..88dd806 100644 --- a/codewave_core/closing_promp.py +++ b/codewave_core/closing_promp.py @@ -5,6 +5,7 @@ class ClosingPromp(): def __init__(self, codewave,selections): self.codewave = codewave self._typed = None + self.nbChanges = 0 self.selections = util.PosCollection(selections) def begin(self): self.started = True @@ -24,6 +25,7 @@ def onChange(self,ch = None): self.invalidTyped() if self.skipEvent(ch): return + self.nbChanges+=1 if self.shouldStop(): self.stop() self.cleanClose() @@ -44,13 +46,14 @@ def cleanClose(self): selections = self.getSelections() start = None for sel in selections: + sel.withEditor(self.codewave.editor) pos = self.whithinOpenBounds(sel) if pos is not None: start = sel elif start is not None: end = self.whithinCloseBounds(sel) if end is not None: - res = end.innerTextFromEditor(self.codewave.editor).split(' ')[0] + res = end.innerText().split(' ')[0] repl = util.Replacement(end.innerStart,end.innerEnd,res) repl.selections = [start] replacements.append(repl) @@ -71,15 +74,16 @@ def cancelSelections(self,selections): replacements = [] start = None for sel in selections: + sel.withEditor(self.codewave.editor) pos = self.whithinOpenBounds(sel) if pos is not None: start = pos else: end = self.whithinCloseBounds(sel) if end is not None and start is not None: - start = None replacements.append(util.Replacement(start.start,end.end,self.codewave.editor.textSubstr(start.end+1, end.start-1)).selectContent()) - self.codewave.editor.applyReplacements(self.replacements) + start = None + self.codewave.editor.applyReplacements(replacements) def typed(self): if self._typed is None: cpos = self.codewave.editor.getCursorPos() @@ -97,26 +101,26 @@ def whithinOpenBounds(self,pos): for i, repl in enumerate(self.replacements): targetPos = self.startPosAt(i) targetText = self.codewave.brakets + (self.typed() or '') + self.codewave.brakets - if targetPos.innerContainsPos(pos) and targetPos.textFromEditor(self.codewave.editor) == targetText: + if targetPos.innerContainsPos(pos) and targetPos.text() == targetText: return targetPos return None def whithinCloseBounds(self,pos): for i, repl in enumerate(self.replacements): targetPos = self.endPosAt(i) targetText = self.codewave.brakets + self.codewave.closeChar + self.typed() + self.codewave.brakets - if targetPos.innerContainsPos(pos) and targetPos.textFromEditor(self.codewave.editor) == targetText: + if targetPos.innerContainsPos(pos) and targetPos.text() == targetText: return targetPos return None def startPosAt(self,index): return util.Pos( self.replacements[index].selections[0].start + len(self.typed() or '') * (index*2), self.replacements[index].selections[0].end + len(self.typed() or '') * (index*2 +1) - ).wrappedBy(self.codewave.brakets, self.codewave.brakets) + ).wrappedBy(self.codewave.brakets, self.codewave.brakets).withEditor(self.codewave.editor) def endPosAt(self,index): return util.Pos( self.replacements[index].selections[1].start + len(self.typed()) * (index*2 +1), self.replacements[index].selections[1].end + len(self.typed()) * (index*2 +2) - ).wrappedBy(self.codewave.brakets + self.codewave.closeChar, self.codewave.brakets) + ).wrappedBy(self.codewave.brakets + self.codewave.closeChar, self.codewave.brakets).withEditor(self.codewave.editor) class SimulatedClosingPromp(ClosingPromp): def resume(self): diff --git a/codewave_core/cmd_finder.py b/codewave_core/cmd_finder.py index eecec90..d9bc6b5 100644 --- a/codewave_core/cmd_finder.py +++ b/codewave_core/cmd_finder.py @@ -1,12 +1,17 @@ import codewave_core.command as command -import codewave_core.core_cmds as core_cmds import codewave_core.logger as logger import codewave_core.util as util import codewave_core.context as context +import codewave_core.cmds.core +import codewave_core.cmds.html +import codewave_core.cmds.js +import codewave_core.cmds.php + class CmdFinder(): def __init__(self,names, **options): - + + self.posibilities = None if not util.isArray(names): names = [names] defaults = { @@ -40,10 +45,10 @@ def find(self): self.triggerDetectors() self.cmd = self.findIn(self.root) return self.cmd - def getPosibilities(self): - self.triggerDetectors() - path = list(self.path) - return self.findPosibilitiesIn(self.root,path) +# def getPosibilities(self): +# self.triggerDetectors() +# path = list(self.path) +# return self.findPosibilitiesIn(self.root,path) def getNamesWithPaths(self): paths = {} for name in self.names : @@ -90,13 +95,13 @@ def findPosibilities(self): self.root.init() posibilities = [] for space, names in self.getNamesWithPaths().items(): - next = self.getCmdFollowAlias(space) - if next is not None : + nexts = self.getCmdFollowAlias(space) + for next in nexts: posibilities += CmdFinder(names, parent=self, root=next).findPosibilities() for nspc in self.context.getNameSpaces(): nspcName,rest = util.splitFirstNamespace(nspc,True) - next = self.getCmdFollowAlias(nspcName) - if next is not None : + nexts = self.getCmdFollowAlias(nspcName) + for next in nexts: posibilities += CmdFinder(self.applySpaceOnNames(nspc), parent=self, root=next).findPosibilities() for name in self.getDirectNames(): direct = self.root.getCmd(name) @@ -106,18 +111,25 @@ def findPosibilities(self): fallback = self.root.getCmd('fallback') if self.cmdIsValid(fallback): posibilities.append(fallback) + self.posibilities = posibilities return posibilities def getCmdFollowAlias(self,name): cmd = self.root.getCmd(name) if cmd is not None : cmd.init() if cmd.aliasOf is not None: - return cmd.getAliased() - return cmd + return [cmd,cmd.getAliased()] + return [cmd] def cmdIsValid(self,cmd): if cmd is None: return False + if cmd.name != 'fallback' and cmd in self.ancestors(): + return False return not self.mustExecute or self.cmdIsExecutable(cmd) + def ancestors(self): + if self.codewave is not None and self.codewave.inInstance is not None: + return self.codewave.inInstance.ancestorCmdsAndSelf() + return [] def cmdIsExecutable(self,cmd): names = self.getDirectNames() if len(names) == 1: diff --git a/codewave_core/cmd_instance.py b/codewave_core/cmd_instance.py index fa1d4e9..bd01705 100644 --- a/codewave_core/cmd_instance.py +++ b/codewave_core/cmd_instance.py @@ -10,10 +10,11 @@ class CmdInstance(object): def __init__(self, cmd = None, context = None): self.cmd,self.context = cmd,context self.content = self.cmdObj = None - self.indentLen = self.cmd = self.aliasedCmd = self.aliasedFinalCmd = self.cmdOptions = None + self.inited = False + self.indentLen = self.aliasedCmd = self.aliasedFinalCmd = self.cmdOptions = None def init(self): - if not self.isEmpty() or self.inited: + if not self.isEmpty() and not self.inited: self.inited = True self._getCmdObj() self._initParams() @@ -43,7 +44,7 @@ def _getCmdObj(self): def _initParams(self): self.named = self.getDefaults() def _getParentNamespaces(self): - return array() + return [] def isEmpty(self): return self.cmd is not None def resultIsAvailable(self): @@ -61,7 +62,7 @@ def getDefaults(self): aliased = self.getAliased() if aliased is not None: res = util.merge(res,aliased.getDefaults()) - res = util.merge(res,cmd.defaults) + res = util.merge(res,self.cmd.defaults) if self.cmdObj is not None: res = util.merge(res,self.cmdObj.getDefaults()) return res @@ -77,13 +78,13 @@ def getAliasedFinal(self): if self.cmd.aliasOf is not None: aliased = self.cmd while aliased is not None and aliased.aliasOf is not None: - nspc, cmdName = util.splitNamespace(self.cmdName) - aliasOf = aliased.aliasOf.replace('%name%',cmdName) - aliased = aliased._aliasedFromFinder(self.getFinder(aliasOf)) + aliased = aliased._aliasedFromFinder(self.getFinder(self.alterAliasOf(aliased.aliasOf))) if self.aliasedCmd is None: self.aliasedCmd = aliased or False self.aliasedFinalCmd = aliased or False return aliased + def alterAliasOf(self,aliasOf): + return aliasOf def getOptions(self): if self.cmd is not None: if self.cmdOptions is not None: @@ -98,7 +99,7 @@ def getOption(self,key): if options is not None and key in options: return options[key] def getParam(self,names, defVal = None): - if type(names) is not list : + if not util.isArray(names) : names = [names] for n in names: if isinstance( n, int ) and n < len(self.params) : @@ -106,6 +107,12 @@ def getParam(self,names, defVal = None): if n in self.named : return self.named[n] return defVal + def ancestorCmds(self): + if self.context.codewave is not None and self.context.codewave.inInstance is not None: + return self.context.codewave.inInstance.ancestorCmdsAndSelf() + return [] + def ancestorCmdsAndSelf(self): + return self.ancestorCmds() + [self.cmd] def runExecuteFunct(self): if self.cmd is not None: if self.cmdObj is not None: @@ -149,8 +156,4 @@ def formatIndent(self,text): else: return text def applyIndent(self,text): - if text is not None: - reg = re.compile(r'\n',re.M) - return re.sub(reg, "\n" + util.repeatToLength(" ",self.getIndent()),text) - else: - return text \ No newline at end of file + return util.indentNotFirst(text,self.getIndent()," ") \ No newline at end of file diff --git a/codewave_core/cmds/__init__.py b/codewave_core/cmds/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/codewave_core/core_cmds.py b/codewave_core/cmds/core.py similarity index 80% rename from codewave_core/core_cmds.py rename to codewave_core/cmds/core.py index 8d23e22..5375383 100644 --- a/codewave_core/core_cmds.py +++ b/codewave_core/cmds/core.py @@ -135,7 +135,7 @@ def initCmds(): 'cmds' : { 'intro':{ 'result' : textwrap.dedent(""" - Codewave allows you to make you own commands (or abbreviations) + Codewave allows you to make your own commands (or abbreviations) put your content inside "source" the do "save". Try adding any text that is on your mind. ~~!edit my_new_command|~~ @@ -201,6 +201,9 @@ def initCmds(): 'close':{ 'cls' : CloseCmd }, + 'param':{ + 'result' : getParam + }, 'edit':{ 'cmds' : editCmdSetCmds({ 'save':{ @@ -269,111 +272,6 @@ def initCmds(): }) - html = command.cmds.addCmd(command.Command('html')) - html.addCmds({ - 'fallback':{ - 'aliasOf' : 'core:emmet', - 'defaults' : {'lang':'html'}, - 'nameToParam' : 'abbr' - }, - }) - - css = command.cmds.addCmd(command.Command('css')) - css.addCmds({ - 'fallback':{ - 'aliasOf' : 'core:emmet', - 'defaults' : {'lang':'css'}, - 'nameToParam' : 'abbr' - }, - }) - - php = command.cmds.addCmd( command.Command('php')) - php.addDetector(detector.PairDetector({ - 'result': 'php:inner', - 'opener': '', - 'else': 'php:outer' - })) - - phpOuter = php.addCmd(command.Command('outer')) - phpOuter.addCmds({ - 'fallback':{ - 'aliasOf' : 'php:inner:%name%', - 'beforeExecute' : closePhpForContent, - 'alterResult' : wrapWithPhp - }, - 'comment': '', - 'php': '', - }) - - phpInner = php.addCmd(command.Command('inner')) - phpInner.addCmds({ - 'comment': '/* ~~content~~ */', - 'if': 'if(|){\n\t~~content~~\n}', - 'info': 'phpinfo();', - 'echo': 'echo ${id}', - 'e':{ 'aliasOf': 'php:inner:echo' }, - 'class': textwrap.dedent(""" - class | { - \tfunction __construct() { - \t\t~~content~~ - \t} - } - """), - 'c':{ 'aliasOf': 'php:inner:class' }, - 'function': 'function |() {\n\t~~content~~\n}', - 'funct':{ 'aliasOf': 'php:inner:function' }, - 'f':{ 'aliasOf': 'php:inner:function' }, - 'array': '$| = array();', - 'a': 'array()', - 'for': 'for ($i = 0; $i < $|; $i+=1) {\n\t~~content~~\n}', - 'foreach':'foreach ($| as $key => $val) {\n\t~~content~~\n}', - 'each':{ 'aliasOf': 'php:inner:foreach' }, - 'while': 'while(|) {\n\t~~content~~\n}', - 'whilei': '$i = 0;\nwhile(|) {\n\t~~content~~\n\t$i+=1;\n}', - 'ifelse': 'if( | ) {\n\t~~content~~\n} else {\n\t\n}', - 'ife':{ 'aliasOf': 'php:inner:ifelse' }, - 'switch': textwrap.dedent(""" - switch( | ) { - \tcase : - \t\t~~content~~ - \t\tbreak; - \tdefault : - \t\t - \t\tbreak; - } - """) - }) - - js = command.cmds.addCmd(command.Command('js')) - command.cmds.addCmd(command.Command('javascript',{ 'aliasOf': 'js' })) - js.addCmds({ - 'comment': '/* ~~content~~ */', - 'if': 'if(|){\n\t~~content~~\n}', - 'log': 'if(window.console){\n\tconsole.log(~~content~~|)\n}', - 'function': 'function |() {\n\t~~content~~\n}', - 'funct':{ 'aliasOf': 'js:function' }, - 'f':{ 'aliasOf': 'js:function' }, - 'for': 'for (var i = 0; i < |; i+=1) {\n\t~~content~~\n}', - 'forin':'foreach (var val in |) {\n\t~~content~~\n}', - 'each':{ 'aliasOf': 'js:forin' }, - 'foreach':{ 'aliasOf': 'js:forin' }, - 'while': 'while(|) {\n\t~~content~~\n}', - 'whilei': 'var i = 0;\nwhile(|) {\n\t~~content~~\n\ti+=1;\n}', - 'ifelse': 'if( | ) {\n\t~~content~~\n} else {\n\t\n}', - 'ife':{ 'aliasOf': 'js:ifelse' }, - 'switch': textwrap.dedent(""" - switch( | ) { - \tcase : - \t\t~~content~~ - \t\tbreak; - \tdefault : - \t\t - \t\tbreak; - } - """), - }) - command.cmdInitialisers.add(initCmds) def setVarCmd(name, base = {}) : @@ -417,19 +315,19 @@ def exec_parent(instance): instance.replaceEnd = instance.parent.replaceEnd return res def getContent(instance): + affixes_empty = instance.getParam(['affixes_empty'],False) + prefix = instance.getParam(['prefix'],'') + suffix = instance.getParam(['suffix'],'') if instance.codewave.inInstance is not None: - return instance.codewave.inInstance.content or '' -def wrapWithPhp(result,instance): - regOpen = re.compile(r"<\?php\s([\n\r\s]+)") - regClose = re.compile(r"([\n\r\s]+)\s\?>") - return '\1', result)) + ' ?>' + return prefix + (instance.codewave.inInstance.content or '') + suffix + if affixes_empty: + return prefix + suffix def renameCommand(instance): savedCmds = storage.load('cmds') origninalName = instance.getParam([0,'from']) newName = instance.getParam([1,'to']) if origninalName is not None and newName is not None: cmd = instance.context.getCmd(origninalName) - console.log(cmd) if origninalName in savedCmds and cmd is not None: if not ':' in newName: newName = cmd.fullName.replace(origninalName,'') + newName @@ -468,12 +366,15 @@ def aliasCommand(instance): cmd = cmd.getAliased() or cmd # unless ':' in alias # alias = cmd.fullName.replace(name,'') + alias - command.saveCmd(alias, { aliasOf: cmd.fullName }) + command.saveCmd(alias, { 'aliasOf': cmd.fullName }) return "" else: return "~~not_found~~" -def closePhpForContent(instance): - instance.content = ' ?>'+(instance.content or '')+' box2.end + len(suffix)): + box = box2 if box is not None: - self.instance.codewave.editor.spliceText(box.start,box.end,'') - self.instance.codewave.editor.setCursorPos(box.start) + depth = self.helper.getNestedLvl(self.instance.getPos().start) + if depth < 2: + self.instance.inBox = None + self.instance.applyReplacement(util.Replacement(box.start,box.end,'')) else: self.instance.replaceWith('') @@ -546,18 +459,17 @@ def init(self): self.finder.useFallbacks = False self.cmd = self.finder.find() self.editable = self.cmd.isEditable() if self.cmd is not None else True - self.content = self.instance.content def getOptions(self): return { 'allowedNamed': ['cmd'] } def result(self): - if self.content: + if self.instance.content: return self.resultWithContent() else: return self.resultWithoutContent() def resultWithContent(self): - parser = self.instance.getParserForText(self.content) + parser = self.instance.getParserForText(self.instance.content) parser.parseAll() data = {} for p in editCmdProps: diff --git a/codewave_core/cmds/html.py b/codewave_core/cmds/html.py new file mode 100644 index 0000000..d5508e7 --- /dev/null +++ b/codewave_core/cmds/html.py @@ -0,0 +1,30 @@ +import re +import textwrap +import codewave_core.command as command +import codewave_core.util as util +import codewave_core.logger as logger +import codewave_core.detector as detector +import codewave_core.box_helper as box_helper +import codewave_core.edit_cmd_prop as edit_cmd_prop + + +def initCmds(): + html = command.cmds.addCmd(command.Command('html')) + html.addCmds({ + 'fallback':{ + 'aliasOf' : 'core:emmet', + 'defaults' : {'lang':'html'}, + 'nameToParam' : 'abbr' + }, + }) + + css = command.cmds.addCmd(command.Command('css')) + css.addCmds({ + 'fallback':{ + 'aliasOf' : 'core:emmet', + 'defaults' : {'lang':'css'}, + 'nameToParam' : 'abbr' + }, + }) + +command.cmdInitialisers.add(initCmds) diff --git a/codewave_core/cmds/js.py b/codewave_core/cmds/js.py new file mode 100644 index 0000000..b2e0415 --- /dev/null +++ b/codewave_core/cmds/js.py @@ -0,0 +1,42 @@ +import re +import textwrap +import codewave_core.command as command +import codewave_core.util as util +import codewave_core.logger as logger +import codewave_core.detector as detector +import codewave_core.box_helper as box_helper +import codewave_core.edit_cmd_prop as edit_cmd_prop + + +def initCmds(): + js = command.cmds.addCmd(command.Command('js')) + command.cmds.addCmd(command.Command('javascript',{ 'aliasOf': 'js' })) + js.addCmds({ + 'comment': '/* ~~content~~ */', + 'if': 'if(|){\n\t~~content~~\n}', + 'log': 'if(window.console){\n\tconsole.log(~~content~~|)\n}', + 'function': 'function |() {\n\t~~content~~\n}', + 'funct':{ 'aliasOf': 'js:function' }, + 'f':{ 'aliasOf': 'js:function' }, + 'for': 'for (var i = 0; i < |; i+=1) {\n\t~~content~~\n}', + 'forin':'foreach (var val in |) {\n\t~~content~~\n}', + 'each':{ 'aliasOf': 'js:forin' }, + 'foreach':{ 'aliasOf': 'js:forin' }, + 'while': 'while(|) {\n\t~~content~~\n}', + 'whilei': 'var i = 0;\nwhile(|) {\n\t~~content~~\n\ti+=1;\n}', + 'ifelse': 'if( | ) {\n\t~~content~~\n} else {\n\t\n}', + 'ife':{ 'aliasOf': 'js:ifelse' }, + 'switch': textwrap.dedent(""" + switch( | ) { + \tcase : + \t\t~~content~~ + \t\tbreak; + \tdefault : + \t\t + \t\tbreak; + } + """), + }) + +command.cmdInitialisers.add(initCmds) + diff --git a/codewave_core/cmds/php.py b/codewave_core/cmds/php.py new file mode 100644 index 0000000..3160c2f --- /dev/null +++ b/codewave_core/cmds/php.py @@ -0,0 +1,127 @@ +import re +import textwrap +import codewave_core.command as command +import codewave_core.util as util +import codewave_core.logger as logger +import codewave_core.detector as detector +import codewave_core.box_helper as box_helper +import codewave_core.edit_cmd_prop as edit_cmd_prop + + +def initCmds(): + php = command.cmds.addCmd( command.Command('php')) + php.addDetector(detector.PairDetector({ + 'result': 'php:inner', + 'opener': '', + 'optionnal_end': True, + 'else': 'php:outer' + })) + + phpOuter = php.addCmd(command.Command('outer')) + phpOuter.addCmds({ + 'fallback':{ + 'cmds' : { + 'any_content': { + 'aliasOf': 'core:content', + 'defaults': { + 'prefix': ' ?>\n', + 'suffix': '\n' + } + }, + 'comment': '/* ~~content~~ */', + 'php': '', + }) + + phpInner = php.addCmd(command.Command('inner')) + phpInner.addCmds({ + 'any_content': { 'aliasOf': 'core:content' }, + 'comment': '/* ~~content~~ */', + 'if': 'if(|){\n\t~~any_content~~\n}', + 'info': 'phpinfo();', + 'echo': 'echo |', + 'e':{ 'aliasOf': 'php:inner:echo' }, + 'class': { + 'result' : textwrap.dedent(""" + class ~~param 0 class def:|~~ { + \tfunction __construct() { + \t\t~~content~~ + \t} + } + """), + 'defaults': { + 'inline': False + } + }, + 'c':{ 'aliasOf': 'php:inner:class' }, + 'function': { + 'result' : 'function |() {\n\t~~content~~\n}', + 'defaults': { + 'inline': False + } + }, + 'funct':{ 'aliasOf': 'php:inner:function' }, + 'f':{ 'aliasOf': 'php:inner:function' }, + 'array': '$| = array();', + 'a': 'array()', + 'for': 'for ($i = 0; $i < $|; $i+=1) {\n\t~~any_content~~\n}', + 'foreach':'foreach ($| as $key => $val) {\n\t~~any_content~~\n}', + 'each':{ 'aliasOf': 'php:inner:foreach' }, + 'while': 'while(|) {\n\t~~any_content~~\n}', + 'whilei': { + 'result' : '$i = 0;\nwhile(|) {\n\t~~any_content~~\n\t$i+=1;\n}', + 'defaults': { + 'inline': False + } + }, + 'ifelse': 'if( | ) {\n\t~~any_content~~\n} else {\n\t\n}', + 'ife':{ 'aliasOf': 'php:inner:ifelse' }, + 'switch': { + 'result' : textwrap.dedent(""" + switch( | ) { + \tcase : + \t\t~~content~~ + \t\tbreak; + \tdefault : + \t\t + \t\tbreak; + } + """), + 'defaults': { + 'inline': False + } + }, + 'close': { + 'aliasOf': 'core:close', + 'defaults': { + 'prefix': '', + 'required_affixes': False + } + }, + }) + + +command.cmdInitialisers.add(initCmds) + + +def wrapWithPhp(result,instance): + inline = instance.getParam(['php_inline','inline'],True) + if inline: + regOpen = re.compile(r"<\?php\s([\n\r\s]+)") + regClose = re.compile(r"([\n\r\s]+)\s\?>") + return '\1', result)) + ' ?>' + else: + return '' diff --git a/codewave_core/codewave.py b/codewave_core/codewave.py index e35fe3c..bcb3cd6 100644 --- a/codewave_core/codewave.py +++ b/codewave_core/codewave.py @@ -16,6 +16,7 @@ class Codewave(): def __init__(self,editor, **options): self.editor = editor self.closingPromp = self.context = None + init() self.marker = '[[[[codewave_marquer]]]]' self.vars = {} @@ -29,6 +30,9 @@ def __init__(self,editor, **options): 'inInstance' : None } self.parent = options['parent'] if 'parent' in options else None + + + self.nested = self.parent.nested+1 if self.parent is not None else 0 for key, val in defaults.items(): if key in options: @@ -58,7 +62,7 @@ def runAtPos(self,pos): def runAtMultiPos(self,multiPos): if len(multiPos) > 0: cmd = self.commandOnPos(multiPos[0].end) - if cmd is not None : + if cmd is not None: if len(multiPos) > 1: cmd.setMultiPos(multiPos) cmd.init() @@ -126,13 +130,6 @@ def countPrevBraket(self,start): return i def isEndLine(self,pos): return self.editor.textSubstr(pos,pos+1) == "\n" or pos + 1 >= self.editor.textLen() - def findLineStart(self,pos): - p = self.findAnyNext(pos ,["\n"], -1) - return p.pos+1 if p is not None else 0 - - def findLineEnd(self,pos): - p = self.findAnyNext(pos ,["\n","\r"]) - return p.pos if p is not None else self.editor.textLen() def findPrevBraket(self,start): return self.findNextBraket(start,-1) def findNextBraket(self,start,direction = 1): @@ -146,20 +143,7 @@ def findNext(self,start,string,direction = 1): if f is not None: return f.pos def findAnyNext(self,start,strings,direction = 1): - if direction > 0: - text = self.editor.textSubstr(start,self.editor.textLen()) - else: - text = self.editor.textSubstr(0,start) - bestPos = bestStr = None - for stri in strings: - pos = text.find(stri) if direction > 0 else text.rfind(stri) - if pos != -1: - if not bestPos is not None or bestPos*direction > pos*direction: - bestPos = pos - bestStr = stri - if bestStr is not None: - return util.StrPos((bestPos + start if direction > 0 else bestPos),bestStr) - return None + return self.editor.findAnyNext(start,strings,direction) def findMatchingPair(self,startPos,opening,closing,direction = 1): pos = startPos nested = 0 @@ -222,6 +206,10 @@ def regMarker(self,flags=0): def removeMarkers(self,text): return text.replace(self.marker,'') +inited = False def init(): - command.initCmds() - command.loadCmds() \ No newline at end of file + global inited + if not inited: + inited = True + command.initCmds() + command.loadCmds() \ No newline at end of file diff --git a/codewave_core/edit_cmd_prop.py b/codewave_core/edit_cmd_prop.py index f5442a9..9977f84 100644 --- a/codewave_core/edit_cmd_prop.py +++ b/codewave_core/edit_cmd_prop.py @@ -1,6 +1,6 @@ import codewave_core.command as command -import codewave_core.core_cmds +import codewave_core.cmds.core class EditCmdProp(object): def __init__(self, name,options): @@ -23,10 +23,10 @@ def __init__(self, name,options): setattr(self,key,val) def setCmd(self,cmds): - cmds[self.name] = codewave_core.core_cmds.setVarCmd(self.name) + cmds[self.name] = codewave_core.cmds.core.setVarCmd(self.name) def writeFor(self,parser,obj): - if parser.vars[self.name] is not None: + if self.name in parser.vars: obj[self.dataName] = parser.vars[self.name] def valFromCmd(self,cmd): if cmd is not None: @@ -53,7 +53,7 @@ def valFromCmd(self,cmd): res = res.replace('|', '||') return res def setCmd(self,cmds): - cmds[self.name] = codewave_core.core_cmds.setVarCmd(self.name,{'preventParseAll' : True}) + cmds[self.name] = codewave_core.cmds.core.setVarCmd(self.name,{'preventParseAll' : True}) def showForCmd(self,cmd): val = self.valFromCmd(cmd) return (self.showEmpty and not(cmd is not None and cmd.aliasOf is not None)) or val is not None @@ -67,9 +67,9 @@ def display(self,cmd): class revBool(EditCmdProp): def setCmd(self,cmds): - cmds[self.name] = codewave_core.core_cmds.setBoolVarCmd(self.name) + cmds[self.name] = codewave_core.cmds.core.setBoolVarCmd(self.name) def writeFor(self,parser,obj): - if parser.vars[self.name] is not None: + if self.name in parser.vars: obj[self.dataName] = not parser.vars[self.name] def display(self,cmd): val = self.valFromCmd(cmd) @@ -79,7 +79,7 @@ def display(self,cmd): class bool(EditCmdProp): def setCmd(self,cmds): - cmds[self.name] = codewave_core.core_cmds.setBoolVarCmd(self.name) + cmds[self.name] = codewave_core.cmds.core.setBoolVarCmd(self.name) def display(self,cmd): if self.valFromCmd(cmd): return "~~!"+self.name+"~~" \ No newline at end of file diff --git a/codewave_core/editor.py b/codewave_core/editor.py index 1ae5e88..b8887dd 100644 --- a/codewave_core/editor.py +++ b/codewave_core/editor.py @@ -1,6 +1,9 @@ -class Editor(): +import codewave_core.util as util + +class Editor(object): def __init__(self): self.namespace = None + self._lang = None def bindedTo(self,codewave): pass @@ -30,7 +33,9 @@ def beginUndoAction(self): def endUndoAction(self): pass def getLang(self): - return None + return self._lang + def setLang(self,val): + self._lang = val def getEmmetContextObject(self): return None def allowMultiSelection(self): @@ -46,12 +51,37 @@ def addChangeListener(self,callback): def removeChangeListener(self,callback): raise NotImplementedError + def getLineAt(self,pos): + return util.Pos(self.findLineStart(pos),self.findLineEnd(pos)) + def findLineStart(self,pos): + p = self.findAnyNext(pos ,["\n"], -1) + return p.pos+1 if p is not None else 0 + def findLineEnd(self,pos): + p = self.findAnyNext(pos ,["\n","\r"]) + return p.pos if p is not None else self.textLen() + + def findAnyNext(self,start,strings,direction = 1): + if direction > 0: + text = self.textSubstr(start,self.textLen()) + else: + text = self.textSubstr(0,start) + bestPos = bestStr = None + for stri in strings: + pos = text.find(stri) if direction > 0 else text.rfind(stri) + if pos != -1: + if not bestPos is not None or bestPos*direction > pos*direction: + bestPos = pos + bestStr = stri + if bestStr is not None: + return util.StrPos((bestPos + start if direction > 0 else bestPos),bestStr) + return None def applyReplacements(self,replacements): selections = [] offset = 0 for repl in replacements: + repl.withEditor(self) repl.applyOffset(offset) - repl.applyToEditor(self) + repl.apply() offset += repl.offsetAfter() selections += repl.selections diff --git a/codewave_core/logger.py b/codewave_core/logger.py index e02768a..dd1f30b 100644 --- a/codewave_core/logger.py +++ b/codewave_core/logger.py @@ -12,8 +12,8 @@ def log(*arguments): out += str(msg)+' ' if WRITE_FUNCT is not None : WRITE_FUNCT(out+'\n') - else : - raise Exception("Logger write function is not setted") + # else : + # raise Exception("Logger write function is not setted") def step(prop): global last_log diff --git a/codewave_core/positioned_cmd_instance.py b/codewave_core/positioned_cmd_instance.py index ace774f..26c4244 100644 --- a/codewave_core/positioned_cmd_instance.py +++ b/codewave_core/positioned_cmd_instance.py @@ -13,7 +13,6 @@ def __init__(self, codewave,pos,str): self.codewave,self.pos,self.str = codewave,pos,str self.replaceStart = self.replaceEnd = None self.multiPos = self.inBox = self.closingPos = None - self._prevEOL = self._nextEOL = self._rawWithFullLines = self._sameLinesPrefix = self._sameLinesSuffix = None self.inited = False if not self.isEmpty(): self._checkCloser() @@ -44,9 +43,8 @@ def _splitComponents(self): self.rawParams = " ".join(parts) def _parseParams(self,params): self.params = [] - self.named = {} + self.named = self.getDefaults() if self.cmd is not None: - self.named.update(self.cmd.getDefaults()) nameToParam = self.getOption('nameToParam') if nameToParam is not None : self.named[nameToParam] = self.cmdName @@ -108,7 +106,7 @@ def _checkBox(self): self.pos = self.pos - len(cl) self.str = self.codewave.editor.textSubstr(self.pos,endPos) self._removeCommentFromContent() - elif cl in self.sameLinesPrefix() and cr in self.sameLinesSuffix(): + elif cl in self.getPos().sameLinesPrefix() and cr in self.getPos().sameLinesSuffix(): self.inBox = 1 self._removeCommentFromContent() def _removeCommentFromContent(self): @@ -125,26 +123,6 @@ def _getParentCmds(self): self.parent = p.init() if p is not None else None def setMultiPos(self,multiPos): self.multiPos = multiPos - def prevEOL(self): - if self._prevEOL is None: - self._prevEOL = self.codewave.findLineStart(self.pos) - return self._prevEOL - def nextEOL(self): - if self._nextEOL is None: - self._nextEOL = self.codewave.findLineEnd(self.getEndPos()) - return self._nextEOL - def rawWithFullLines(self): - if self._rawWithFullLines is None: - self._rawWithFullLines = self.codewave.editor.textSubstr(self.prevEOL(),self.nextEOL()) - return self._rawWithFullLines - def sameLinesPrefix(self): - if self._sameLinesPrefix is None: - self._sameLinesPrefix = self.codewave.editor.textSubstr(self.prevEOL(),self.pos) - return self._sameLinesPrefix - def sameLinesSuffix(self): - if self._sameLinesSuffix is None: - self._sameLinesSuffix = self.codewave.editor.textSubstr(self.getEndPos(),self.nextEOL()) - return self._sameLinesSuffix def _getCmdObj(self): self.getCmd() self._checkBox() @@ -181,12 +159,16 @@ def _getParentNamespaces(self): return nspcs def _removeBracket(self,str): return str[len(self.codewave.brakets):len(str)-len(self.codewave.brakets)] + def alterAliasOf(self,aliasOf): + nspc, cmdName = util.splitNamespace(self.cmdName) + return aliasOf.replace('%name%',cmdName) def isEmpty(self): return self.str == self.codewave.brakets + self.codewave.closeChar + self.codewave.brakets or self.str == self.codewave.brakets + self.codewave.brakets def execute(self): if self.isEmpty(): - if self.codewave.closingPromp is not None and self.codewave.closingPromp.whithinOpenBounds(self.pos + len(self.codewave.brakets)) is not None: + if self.codewave.closingPromp is not None and self.codewave.closingPromp.whithinOpenBounds(util.Pos(self.pos + len(self.codewave.brakets))) is not None: self.codewave.closingPromp.cancel() + pass else: self.replaceWith('') elif self.cmd is not None: @@ -203,14 +185,16 @@ def execute(self): def getEndPos(self): return self.pos+len(self.str) def getPos(self): - return util.Pos(self.pos,self.pos+len(self.str)) + return util.Pos(self.pos,self.pos+len(self.str)).withEditor(self.codewave.editor) + def getOpeningPos(self): + return util.Pos(self.pos,self.pos+len(self.opening)).withEditor(self.codewave.editor) def getIndent(self): if self.indentLen is None: if self.inBox is not None: helper = box_helper.BoxHelper(self.context) - self.indentLen = len(helper.removeComment(self.sameLinesPrefix())) + self.indentLen = len(helper.removeComment(self.getPos().sameLinesPrefix())) else: - self.indentLen = self.pos - self.codewave.findLineStart(self.pos) + self.indentLen = self.pos - self.getPos().prevEOL() return self.indentLen def removeIndentFromContent(self,text): if text is not None: @@ -219,18 +203,19 @@ def removeIndentFromContent(self,text): else: return text def alterResultForBox(self,repl): + original = repl.copy() helper = box_helper.BoxHelper(self.context) - helper.getOptFromLine(self.rawWithFullLines(),False) + helper.getOptFromLine(original.textWithFullLines(),False) if self.getOption('replaceBox'): - box = helper.getBoxForPos(self.getPos()) + box = helper.getBoxForPos(original) repl.start, repl.end = box.start, box.end self.indentLen = helper.indent repl.text = self.applyIndent(repl.text) else: repl.text = self.applyIndent(repl.text) - repl.start = self.prevEOL() - repl.end = self.nextEOL() - res = helper.reformatLines(self.sameLinesPrefix() + self.codewave.marker + repl.text + self.codewave.marker + self.sameLinesSuffix(), {'multiline':False}) + repl.start = original.prevEOL() + repl.end = original.nextEOL() + res = helper.reformatLines(original.sameLinesPrefix() + self.codewave.marker + repl.text + self.codewave.marker + original.sameLinesSuffix(), {'multiline':False}) repl.prefix,repl.text,repl.suffix = res.split(self.codewave.marker) return repl def getCursorFromResult(self,repl): @@ -244,26 +229,26 @@ def getCursorFromResult(self,repl): def checkMulti(self,repl): if self.multiPos is not None and len(self.multiPos) > 1: replacements = [repl] - originalText = repl.originalTextWith(self.codewave.editor) + originalText = repl.originalText() for i, pos in enumerate(self.multiPos): if i == 0: originalPos = pos.start else: newRepl = repl.copy().applyOffset(pos.start-originalPos) - if newRepl.originalTextWith(self.codewave.editor) == originalText: + if newRepl.originalText() == originalText: replacements.append(newRepl) - logger.log(replacements) return replacements else: return [repl] def replaceWith(self,text): - repl = util.Replacement(self.pos,self.getEndPos(),text) - + self.applyReplacement(util.Replacement(self.pos,self.getEndPos(),text)) + def applyReplacement(self,repl): + repl.withEditor(self.codewave.editor) if self.inBox is not None: self.alterResultForBox(repl) else: repl.text = self.applyIndent(repl.text) - + cursorPos = self.getCursorFromResult(repl) repl.selections = [util.Pos(cursorPos, cursorPos)] replacements = self.checkMulti(repl) diff --git a/codewave_core/text_parser.py b/codewave_core/text_parser.py index fc5f318..0aa82ff 100644 --- a/codewave_core/text_parser.py +++ b/codewave_core/text_parser.py @@ -6,6 +6,7 @@ def __init__(self,text): self._text = text self.namespace = 'text_parser' self.target = None + super(self.__class__, self).__init__() @property def text(self): return self._text diff --git a/codewave_core/util.py b/codewave_core/util.py index abac3a2..65db0b3 100644 --- a/codewave_core/util.py +++ b/codewave_core/util.py @@ -7,34 +7,75 @@ def __init__(self,pos,str): def end(self) : self.pos + len(self.str) -class Pos(): - def __init__(self,start,end): +class Pos(object): + def __init__(self,start,end = None): + if end is None : + end = start self.start,self.end = start,end + self.initVars() + def initVars(self): + self._editor = None + self._prevEOL = self._nextEOL = self._textWithFullLines = self._sameLinesPrefix = self._sameLinesSuffix = None def containsPt(self,pt): return self.start <= pt and pt <= self.end def containsPos(self,pos): return self.start <= pos.start and pos.end <= self.end def wrappedBy(self,prefix,suffix): return WrappedPos(self.start - len(prefix), self.start, self.end, self.end + len(suffix)) - def textFromEditor(self,editor): - return editor.textSubstr(self.start, self.end) + def withEditor(self,val): + self._editor = val + return self + def editor(self): + if self._editor is None: + raise NameError('No editor set') + return self._editor + def hasEditor(self): + return self._editor is not None + def text(self): + return self.editor().textSubstr(self.start, self.end) def applyOffset(self,offset): if offset != 0: self.start += offset self.end += offset return self + def prevEOL(self): + if self._prevEOL is None: + self._prevEOL = self.editor().findLineStart(self.start) + return self._prevEOL + def nextEOL(self): + if self._nextEOL is None: + self._nextEOL = self.editor().findLineEnd(self.end) + return self._nextEOL + def textWithFullLines(self): + if self._textWithFullLines is None: + self._textWithFullLines = self.editor().textSubstr(self.prevEOL(),self.nextEOL()) + return self._textWithFullLines + def sameLinesPrefix(self): + if self._sameLinesPrefix is None: + self._sameLinesPrefix = self.editor().textSubstr(self.prevEOL(),self.start) + return self._sameLinesPrefix + def sameLinesSuffix(self): + if self._sameLinesSuffix is None: + self._sameLinesSuffix = self.editor().textSubstr(self.end,self.nextEOL()) + return self._sameLinesSuffix def copy(self): - return Pos(self.start,self.end) + res = Pos(self.start,self.end) + if self.hasEditor(): + res.withEditor(self.editor()) + return res + def raw(self): + return [self.start,self.end] class WrappedPos(Pos): - def __init__(self,start,innerStart,innerEnd,end): + def __init__(self, start,innerStart,innerEnd,end): self.start,self.innerStart,self.innerEnd,self.end = start,innerStart,innerEnd,end + self.initVars() def innerContainsPt(self,pt): return self.innerStart <= pt and pt <= self.innerEnd def innerContainsPos(self,pos): return self.innerStart <= pos.start and pos.end <= self.innerEnd - def innerTextFromEditor(self,editor): - return editor.textSubstr(self.innerStart, self.innerEnd) + def innerText(self): + return self.editor().textSubstr(self.innerStart, self.innerEnd) def setInnerLen(self,len): self.moveSufix(self.innerStart + len) def moveSuffix(self,pt): @@ -45,24 +86,52 @@ def copy(self): return WrappedPos(self.start,self.innerStart,self.innerEnd,self.end) class Size(): - def __init__(self,width,height): + def __init__(self, width,height): self.width,self.height = width,height -class Replacement(object): - def __init__(self, start, end, text, prefix ='', suffix = ''): - self.start, self.end, self.text, self.prefix, self.suffix = start, end, text, prefix, suffix - self.selections = [] +class OptionObject(): + def setOpts(self,options,defaults): + self.defaults = defaults + for key, val in self.defaults.items(): + if key in options: + self.setOpt(key,options[key]) + else: + self.setOpt(key,val) + + def setOpt(self,key, val): + setattr(self,key,val) + + def getOpt(self,key): + return getattr(self,key) + + def getOpts(self): + opts = {} + for key, val in self.defaults.items(): + opts[key] = self.getOpt(key) + return opts + +class Replacement(Pos, OptionObject): + def __init__(self, start, end, text, options = {}): + self.start, self.end, self.text, self.options = start, end, text, options + self.setOpts(self.options) + self.initVars() + def setOpts(self,options): + super(Replacement, self).setOpts(options,{ + 'prefix': '', + 'suffix': '', + 'selections': [] + }) def resPosBeforePrefix(self): return self.start+len(self.prefix)+len(self.text) - def resEnd(self,editor = None): - return self.start+len(self.finalText(editor)) - def applyToEditor(self,editor): - editor.spliceText(self.start, self.end, self.finalText(editor)) - def necessaryFor(self,editor): - return self.finalText(editor) != editor.textSubstr(self.start, self.end) - def originalTextWith(self,editor): - return editor.textSubstr(self.start, self.end) - def finalText(self,editor = None): + def resEnd(self): + return self.start+len(self.finalText()) + def apply(self): + self.editor().spliceText(self.start, self.end, self.finalText()) + def necessary(self): + return self.finalText() != self.originalText() + def originalText(self): + return self.editor().textSubstr(self.start, self.end) + def finalText(self): return self.prefix+self.text+self.suffix def offsetAfter(self): return len(self.finalText()) - (self.end - self.start) @@ -94,28 +163,33 @@ def carretToSel(self): return self def copy(self): - res = Replacement(self.start, self.end, self.text, self.prefix, self.suffix) + res = Replacement(self.start, self.end, self.text, self.getOpts()) + if self.hasEditor(): + res.withEditor(self.editor()) res.selections = list(map(lambda s: s.copy(), self.selections)) return res class Wrapping(Replacement): - def __init__(self, start, end, prefix ='', suffix = ''): - self.start, self.end, self.prefix, self.suffix = start, end, prefix, suffix + def __init__(self, start, end, prefix ='', suffix = '', options = {}): + self.start, self.end, self.options = start, end, options + self.setOpts(self.options) self.text = '' - self.selections = [] - def applyToEditor(self,editor): - self.adjustSelFor(editor) - super(self.__class__, self).applyToEditor(editor) - def adjustSelFor(self,editor): - offset = len(self.originalTextWith(editor)) + self.prefix = prefix + self.suffix = suffix + self.initVars() + def apply(self): + self.adjustSel() + super(self.__class__, self).apply() + def adjustSel(self): + offset = len(self.originalText()) for sel in self.selections: if sel.start > self.start+len(self.prefix): sel.start += offset if sel.end >= self.start+len(self.prefix): sel.end += offset - def finalText(self,editor = None): - if editor is not None: - text = self.originalTextWith(editor) + def finalText(self): + if self.hasEditor(): + text = self.originalText() else: text = '' return self.prefix+text+self.suffix @@ -128,9 +202,39 @@ def copy(self): return res +class PairMatch(): + def __init__(self, pair,match,offset = 0): + self.pair,self.match,self.offset = pair,match,offset + self._name = None + def name(self): + if self.match: + if self._name is None: + for i, group in enumerate(self.match.groups()): + if group is not None: + return self.pair.matchAnyPartKeys()[i] + _name = False + return _name or None + def start(self): + return self.match.start() + self.offset + def end(self): + return self.match.end() + self.offset + def valid(self): + return not self.pair.validMatch or self.pair.validMatch(self) + def length(self): + return len(self.match.group(0)) + class Pair(): - def __init__(self, opener, closer, options = {}): - self.opener, self.closer, self.options = opener, closer, options + def __init__(self, opener,closer,options = {}): + self.opener,self.closer,self.options = opener,closer,options + defaults = { + 'optionnal_end': False, + 'validMatch': None + } + for key, val in defaults.items(): + if key in self.options: + setattr(self,key,self.options[key]) + else: + setattr(self,key,val) def openerReg(self): if isinstance(self.opener, str) : return re.compile(escapeRegExp(self.opener)) @@ -156,29 +260,44 @@ def matchAnyReg(self): for key, reg in self.matchAnyParts().items(): groups.append('('+reg.pattern+')') return re.compile('|'.join(groups)) - def matchAny(self,text): - return re.search(self.matchAnyReg(),text) + def matchAny(self,text,offset=0): + while True: + match = self._matchAny(text,offset) + if match is None or match.valid(): + break + offset = match.end() + if match is not None and match.valid(): + return match + def _matchAny(self,text,offset=0): + if offset: + text = text[offset:] + match = re.search(self.matchAnyReg(),text) + if match is not None: + return PairMatch(self,match,offset) def matchAnyNamed(self,text): return self._matchAnyGetName(self.matchAny(text)) - def _matchAnyGetName(self,match): - if match: - for i, group in enumerate(match.groups()): - if group is not None: - return self.matchAnyPartKeys()[i] - return None - def matchAnyLast(self,text): - ctext = text + def matchAnyLast(self,text,offset=0): + res = None while True: - match = self.matchAny(ctext) + match = self.matchAny(text,offset) if match is None: break - ctext = ctext[match.start()+1:] - res = match + offset = match.end() + if res is None or res.end() != match.end(): + res = match return res - def matchAnyLastNamed(self,text): - return self._matchAnyGetName(self.matchAnyLast(text)) + def identical(self): + return self.opener == self.closer + def wrapperPos(self,pos,text): + start = self.matchAnyLast(text[0:pos.start]) + if start is not None and (self.identical() or start.name() == 'opener'): + end = self.matchAny(text,pos.end) + if end is not None and (self.identical() or end.name() == 'closer'): + return Pos(start.start(),end.end()) + elif self.optionnal_end: + return Pos(start.start(),len(text)) def isWapperOf(self,pos,text): - return self.matchAnyNamed(text[pos.end:]) == 'closer' and self.matchAnyLastNamed(text[0:pos.start]) == 'opener' + return self.wrapperPos(pos,text) is not None def splitFirstNamespace(fullname,isSpace = False) : @@ -202,7 +321,9 @@ def escapeRegExp(txt) : def repeatToLength(txt, length): return (txt * (int(length/len(txt))+1))[:length] - + +def repeat(txt, nb): + return txt * nb def getTxtSize(txt): lines = txt.replace('\r','').split("\n") @@ -210,6 +331,23 @@ def getTxtSize(txt): for l in lines: w = max(w,len(l)) return Size(w,len(lines)) + +def indentNotFirst(text,nb=1,spaces=' '): + if text is not None: + reg = re.compile(r'\n',re.M) + return re.sub(reg, "\n" + repeat(spaces, nb), text) + else: + return text + +def indent(text,nb=1,spaces=' '): + if text is not None: + return spaces + indentNotFirst(text,nb,spaces) + else: + return text + +def reverseStr(txt): + return txt[::-1] + def removeCarret(txt, carretChar = '|'): tmp = '[[[[quoted_carret]]]]' return txt.replace(carretChar+carretChar, 'tmp') \ @@ -228,7 +366,7 @@ def getCarretPos(txt, carretChar = '|'): return txt.index(carretChar) def isArray(arr): - return isinstance(arr, list) + return isinstance(arr, list) class PosCollection(object): def __init__(self, obj):