diff -r bc3a34e47923 Lib/idlelib/idle_test/mock_tk.py --- a/Lib/idlelib/idle_test/mock_tk.py Sat Jul 13 04:05:42 2013 -0400 +++ b/Lib/idlelib/idle_test/mock_tk.py Sat Jul 27 01:49:56 2013 +0530 @@ -67,6 +67,7 @@ showwarning = Mbox_func() # None from _tkinter import TclError +import re class Text: """A semi-functional non-gui replacement for tkinter.Text text editors. @@ -93,56 +94,147 @@ "Return string version of index decoded according to current text." return "%s.%s" % self._decode(index, endflag=1) - def _decode(self, index, endflag=0): + def _decode (self,index,endflag=0): """Return a (line, char) tuple of int indexes into self.data. This implements .index without converting the result back to a string. The result is contrained by the number of lines and linelengths of self.data. For many indexes, the result is initally (1, 0). - - The input index may have any of several possible forms: - * line.char float: converted to 'line.char' string; - * 'line.char' string, where line and char are decimal integers; - * 'line.char lineend', where lineend='lineend' (and char is ignored); - * 'line.end', where end='end' (same as above); - * 'insert', the positions before terminal \n; - * 'end', whose meaning depends on the endflag passed to ._endex. - * 'sel.first' or 'sel.last', where sel is a tag -- not implemented. """ if isinstance(index, (float, bytes)): index = str(index) try: index=index.lower() except AttributeError: - raise TclError('bad text index "%s"' % index) from None + raise TclError('bad text index "%s"' % index) lastline = len(self.data) - 1 # same as number of text lines - if index == 'insert': - return lastline, len(self.data[lastline]) - 1 - elif index == 'end': - return self._endex(endflag) + returnline,returnchar,modifier = self._parseBase(index,lastline,endflag) + returnline,returnchar = self._parseModifier(returnline,returnchar,modifier,lastline,endflag) - line, char = index.split('.') - line = int(line) + return returnline, returnchar - # Out of bounds line becomes first or last ('end') index - if line < 1: - return 1, 0 - elif line > lastline: - return self._endex(endflag) - linelength = len(self.data[line]) -1 # position before/at \n - if char.endswith(' lineend') or char == 'end': - return line, linelength - # Tk requires that ignored chars before ' lineend' be valid int + def _parseBase(self,index,lastline,endflag): + """ Break the index into base and modifier and parse the base + return the modifier and line,char decoded by base. + The base of the index may have any of several possible forms: + * line.char float: converted to 'line.char' string; + * 'line.char' string, where line and char are decimal integers; + * 'line.end', where end='end' (same as above); + * 'insert', the positions before terminal \n; + * 'end', whose meaning depends on the endflag passed to ._endex. + * 'sel.first' or 'sel.last', where sel is a tag -- not implemented. + * 'iomark' not implemented (just returns 1,0) + * 'my_anchor' not implemented (just returns 1,0) + """ - # Out of bounds char becomes first or last index of line - char = int(char) - if char < 0: - char = 0 - elif char > linelength: - char = linelength - return line, char + returnline,returnchar,modifier = 1,0,None + matchObj = re.search( r'(insert|end|iomark|my_anchor|sel.first|sel.last|-*\d+.-*\d+|-*\d+.end)(.*)', index, re.M|re.I) + if matchObj: + base = matchObj.group(1) + modifier = matchObj.group(2) + + if base == 'insert': + returnline, returnchar = lastline, len(self.data[lastline]) - 1 + elif base == 'end': + returnline, returnchar = self._endex(endflag) + elif base == 'iomark': + returnline, returnchar = 1,0 + elif base == 'my_anchor': + returnline, returnchar = 1,0 + elif base == 'sel.first': + returnline, returnchar = 1,0 + elif base == 'sel.last': + returnline, returnchar = 1,0 + #make sure the first part of index is line.char format + elif '.' in base: + line, char = base.split('.') + returnline = int(line) + + # Out of bounds line becomes first or last ('end') index + if returnline < 1: + returnline,returnchar = 1, 0 + return returnline,returnchar,modifier + elif returnline > lastline: + returnline, returnchar = self._endex(endflag) + return returnline,returnchar,modifier + + linelength = len(self.data[returnline]) -1 # position before/at \n + if char == 'end': + returnchar = linelength + else: + # Out of bounds char becomes first or last index of line + returnchar = int(char) + if returnchar < 0: + returnchar = 0 + elif returnchar > linelength: + returnchar = linelength + + return returnline,returnchar,modifier + + + def _parseModifier(self,returnline,returnchar,modifier,lastline,endflag): + """ Parse the modifier (the rest of the index returned by _parseBase) + The modifier of the index may have any of several possible forms: + * 'linestart' moves the returnchar to start of the line + * 'lineend' moves the return char to end of the line + * +/- c increases/decreases the return char by specified value + * +/- lines increases/decreases the return line by specified value + """ + if modifier is not None: + #remove spaces, as TK Text allows sapces in or before modifier + modifier = modifier.replace(" ", "") + matchObj = re.search( r'(linestart|lineend)', modifier, re.M|re.I) + if matchObj: + modifier1 = matchObj.group(1) + if modifier1 == 'linestart': + returnchar = 0 + elif modifier1 == 'lineend': + linelength = len(self.data[returnline]) -1 # position before/at \n + returnchar = linelength + + else: + #increment by number of chars or lines + matchObj = re.search( r'(\+|-)(\d*)(c|lines)', modifier, re.M|re.I) + if matchObj: + op = matchObj.group(1) #operator +/- + by = matchObj.group(2) #increase index by + + if matchObj.group(3) == 'lines': + # increase/decrease by lines + returnline = self._increaseIndex(returnline,op,by) + # Out of bounds line becomes first or last ('end') index + if returnline < 1: + returnline,returnchar = 1, 0 + return returnline,returnchar + elif returnline > lastline: + returnline, returnchar = self._endex(endflag) + return returnline,returnchar + + elif matchObj.group(3) == 'c': + #increase/decrease by chars + returnchar = self._increaseIndex(returnchar,op,by) + # Out of bounds char becomes first or last index of line + linelength = len(self.data[returnline]) -1 # position before/at \n + if returnchar < 0: + returnchar = 0 + elif returnchar > linelength: + returnchar = linelength + + return returnline,returnchar + + + def _increaseIndex(self,val,op,by): + ''' + Check the operator and increment amount + and do specific operation accordingly + ''' + if op == '+': + return val + int(by) + elif op == '-': + return val - int(by) + def _endex(self, endflag): '''Return position for 'end' or line overflow corresponding to endflag. diff -r bc3a34e47923 Lib/idlelib/idle_test/test_text.py --- a/Lib/idlelib/idle_test/test_text.py Sat Jul 13 04:05:42 2013 -0400 +++ b/Lib/idlelib/idle_test/test_text.py Sat Jul 27 01:49:56 2013 +0530 @@ -28,6 +28,10 @@ for dex in 'end', 2.0, '2.1', '33.44': self.assertEqual(index(dex), '2.0') + # tests after use of RegularExpression in _decode() + for dex in 'end-1c': + self.assertEqual(index(dex), '2.0') + def test_index_data(self): index = self.text.index self.text.insert('1.0', self.hw)