# Programmer: Daniel Pozmanter # E-mail: drpython@bluebottle.com # Note: You must reply to the verification e-mail to get through. # # Copyright 2003-2005 Daniel Pozmanter # # Distributed under the terms of the GPL (GNU Public License) # # DrPython is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #Functions for getting information about blocks import re reNotEmpty = re.compile('\S') def CheckIfBlockStart(lines, targetline): LineIndent = GetLineIndentation(lines, targetline) if LineIndent == -1: LineIndent = FindValidLine(lines, targetline) if LineIndent == -1: return False #try till the end l = len(lines) li = 1 result = -1 while result == -1: if targetline + li >= l: break result = GetLineIndentation(lines, targetline+li) li += 1 if result > LineIndent: return li - 1 return False def FindValidLine(lines, targetline): #try till the end l = len(lines) li = 1 result = -1 while result == -1: if targetline + li >= l: break result = GetLineIndentation(lines, targetline+li) if result == -1: if targetline - li <= 0: break result = GetLineIndentation(lines, targetline-li) li += 1 return result def RestructureExplicitLineJoining(lines): editedlines = [lines[0]] x = 1 l = len(lines) while x < l: lline = len(lines[x-1]) if lline > 0: if lines[x-1][lline - 1] == '\\': editedlines.append(' ') else: editedlines.append(lines[x]) else: editedlines.append(lines[x]) x += 1 return editedlines def RestructureImplicitLineJoining(lines): editedlines = [] x = 0 l = len(lines) while x < l: p1 = _get_paren_spread(lines[x], '[', ']') p2 = _get_paren_spread(lines[x], '(', ')') p3 = _get_paren_spread(lines[x], '{', '}') end = 0 if p1: end = _find_paren_end_line(lines, x+1, p1, '[', ']') if end == -1: editedlines.extend(lines[x:]) break if p2: tend = _find_paren_end_line(lines, x+1, p2, '(', ')') if tend == -1: editedlines.extend(lines[x:]) break if tend > end: end = tend if p3: tend = _find_paren_end_line(lines, x+1, p3, '{', '}') if tend == -1: editedlines.extend(lines[x:]) break if tend > end: end = tend editedlines.append(lines[x]) if end > 0: newlines, x = _make_lines_whitespace(lines, x+1, end) editedlines.extend(newlines) else: x += 1 return editedlines def RemoveComments(lines): lines = removeStringQuotedWith(lines, "'''") lines = removeStringQuotedWith(lines, '"""') lines = removeStringQuotedWith(lines, "'") lines = removeStringQuotedWith(lines, '"') editedlines = [] for line in lines: c = line.find('#') if c > -1: editedlines.append(line[:c] + ' ') else: editedlines.append(line) return editedlines def removeQuoteFromSingleLine(line, start, target): ft2 = line[start:].find(target) s = reNotEmpty.search(line).start() #Only remove the string if it is not the first bit of the line. if (ft2 > -1) and (start > s): ft2 += start return line[:start] + ' ' + line[:ft2] return None def removeStringQuotedWith(lines, target): editedlines = [] x = 0 l = len(lines) while x < l: ft = lines[x].find(target) if ft > -1: sameline = removeQuoteFromSingleLine(lines[x], ft, target) if sameline is not None: editedlines.append(sameline) else: editedlines.append(lines[x][:ft]) start = x x += 1 end = -1 while (x < l): ft = lines[x].find(target) if ft > -1: end = x break x += 1 if end == -1: return editedlines else: newlines, x = _make_lines_whitespace(lines, start, end) editedlines.extend(newlines) else: editedlines.append(lines[x]) x += 1 return editedlines def spacefill(length): result = '' for x in range(length): result += ' ' return result def _get_paren_spread(text, openp, closep): opencount = text.count(openp) if opencount > 0: closecount = text.count(closep) if opencount > closecount: return opencount - closecount return 0 def _find_paren_end_line(lines, pos, spread, openp, closep): x = pos l = len(lines) count = 0 while x < l: count += lines[x].count(closep) spread += lines[x].count(openp) if count >= spread: return x x += 1 return -1 def _make_lines_whitespace(lines, startline, endline): newlines = [] x = startline while x <= endline: newlines.append(reNotEmpty.sub(' ', lines[x])) x += 1 return newlines, x def GetLines(Document): text = Document.GetText() eol = Document.GetEndOfLineCharacter() lines = text.split(eol) lines = RestructureExplicitLineJoining(lines) lines = RemoveComments(lines) lines = RestructureImplicitLineJoining(lines) return lines def GetBlockStart(lines, targetline): LineIndent = GetLineIndentation(lines, targetline) if LineIndent == -1: LineIndent = FindValidLine(lines, targetline) if LineIndent == -1: return -1 x = targetline - 1 while x > 0: i = GetLineIndentation(lines, x) if i != -1: if i < LineIndent: return x x -= 1 return 0 def GetBlockEnd(lines, targetline): l = len(lines) LineIndent = GetLineIndentation(lines, targetline) if LineIndent == -1: LineIndent = FindValidLine(lines, targetline) if LineIndent == -1: return -1 x = targetline + 1 last = targetline while x < l: i = GetLineIndentation(lines, x) if i != -1: if i >= LineIndent: last = x if i < LineIndent: return last x += 1 if last != x: return last return len(lines) - 1 def GetKeyWordStart(lines, targetline, kword, kwordlength): start = GetBlockStart(lines, targetline) if kwordlength == 0: return start while lines[start].strip()[:kwordlength] != kword: start = GetBlockStart(lines, start) if start == 0: break return start def GetKeyWordEnd(lines, targetline, kword, kwordlength): if (kwordlength == 0) or (lines[targetline].strip()[:kwordlength] == kword): startofblock = CheckIfBlockStart(lines, targetline) if startofblock: targetline += startofblock else: targetline = GetKeyWordStart(lines, targetline, kword, kwordlength) + 1 end = GetBlockEnd(lines, targetline) return end def GetLineIndentation(lines, current): result = reNotEmpty.search(lines[current]) if result is None: return -1 return result.start() def GoToBlockEnd(Document, kword=''): lines = GetLines(Document) kwordl = len(kword) l = GetKeyWordEnd(lines, Document.GetCurrentLine(), kword, kwordl) i = GetLineIndentation(lines, l) if i < 0: i = 0 Document.EnsureVisible(l) Document.GotoLine(l) p = Document.PositionFromLine(l) + i Document.GotoPos(p) Document.SetSTCFocus(True) def GoToBlockStart(Document, kword=''): lines = GetLines(Document) kwordl = len(kword) l = GetKeyWordStart(lines, Document.GetCurrentLine(), kword, kwordl) i = GetLineIndentation(lines, l) if i < 0: i = 0 Document.EnsureVisible(l) Document.GotoLine(l) p = Document.PositionFromLine(l) + i Document.GotoPos(p) Document.SetSTCFocus(True)