Auto Indentation Patch for PyCrust and PyAlaMode

Hi all,

This patch adds auto indentation to the Shell and Editor classes in Py. It
needs some polishing; there is a lot of duplicated code across the Shell and
Editor functions and too many dugging print statements, but I would like to
receive some comments on what I have done so far.

Thanks,

Ben Armston

Index: editor.py

···

===================================================================
RCS file: /pack/cvsroots/wxwidgets/wxWidgets/wxPython/wx/py/editor.py,v
retrieving revision 1.7
diff -u -r1.7 editor.py
--- editor.py 2004/06/14 20:17:31 1.7
+++ editor.py 2005/08/30 20:32:33
@@ -5,6 +5,7 @@
__revision__ = "$Revision: 1.7 $"[11:-2]

import wx
+import re

from buffer import Buffer
import crust
@@ -585,7 +586,28 @@
         # Assign handlers for keyboard events.
         wx.EVT_CHAR(self.window, self.OnChar)
         wx.EVT_KEY_DOWN(self.window, self.OnKeyDown)
+ self.window.Bind(wx.stc.EVT_STC_CHARADDED, self.OnCharAdded, id=self.id)

+ # XXX Later tabwidth and tabChar should be set on a file by file
+ # XXX basis. For existing files, the indentation type should be
+ # XXX determined automatically when the file is opened. If it is
+ # XXX mixed the user should be presented with the option of fixing
+ # XXX it. For new files the user configurable default should be
+ # XXX chosen.
+ self.tabwidth = 4
+ self.tabChar = ' ' * self.tabwidth
+ self.reContainsKeyword = re.compile(r'(\breturn\b)|(\bbreak\b)|'
+ r'(\bpass\b)|(\bcontinue\b)|(\braise\b)')
+ self.reContinuationIndent = re.compile(r'^\s*\S*\s*')
+ eol = self.window.GetEOLMode()
+ if eol == wx.stc.STC_EOL_CR:
+ self.eolMarker = '\r'
+ elif eol == wx.stc.STC_EOL_CRLF:
+ self.eolMarker = '\r\n'
+ else:
+ self.eolMarker = '\n'
+ del eol
+
     def _setBuffer(self, buffer, text):
         """Set the editor to a buffer. Private callback called by buffer."""
         self.buffer = buffer
@@ -695,6 +717,268 @@
             dispatcher.send(signal='FontDefault')
         else:
             event.Skip()
+
+ def OnCharAdded(self, event):
+ """Character added to stc event handler."""
+
+ key = chr(event.GetKey())
+ if key == '\r':
+ if self.window.GetEOLMode() == wx.stc.STC_EOL_CR:
+ self.__autoIndent()
+ event.Skip()
+ elif key == '\n':
+ self.__autoIndent()
+ event.Skip()
+
+ def __autoIndent(self):
+ """Automatically indent or dedent the line."""
+
+ # The algorithm for indenting lines is as follows:
+ #
+ # Check whether the brackets of the line just left are paired:
+ # * Unmatched open brackets
+ # - Find last open bracket
+ # - Indent new line to one space after last open bracket
+ # * Unmatched closed brackets
+ # - Find first unmatched closed bracket
+ # - Find matching opening bracket
+ # - Concatenate intervening lines
+ # - Start indenting this new line from the begining
+ # * No unmatched brackets
+ # - If line above one just left is a \-continuation line
+ # + remove `\'
+ # + concatenate lines
+ # + restart indenting from begining
+ # - elif line just left is \-continuation line
+ # + indent to start of second word
+ # - elif line ends with `:'
+ # + increase indent level
+ # - elif line contains dedenting keyword (e.g. `return')
+ # + if character prior to keyword is `:'
+ # * pass
+ # + else
+ # * decrease indentation level
+
+ print '----------'
+ prevLineNum = self.window.GetCurrentLine() - 1
+ prevLine = self.window.GetLine(prevLineNum)
+ umb = self.__handleUnmatchedBrackets(prevLine, prevLineNum)
+ if umb[0] == 'opening':
+ print 'AUTO INDENT 1: umb =', umb
+ self.window.AddText(self.tabChar * umb[1] + ' ' * umb[2])
+ else:
+ print 'AUTO INDENT 2: umb =', umb
+ line, startLineNum, endLineNum =
self.__handleBackslashIndent(umb[1], umb[2], umb[3])
+ self.__indentLine(line, startLineNum, endLineNum)
+
+ def __handleUnmatchedBrackets(self, line, startLineNum, endLineNum=-1):
+ """
+ Return information required to indent implied continuation lines.
+
+ Return type depends on type of unmatched bracket found.
+
+ * If are unmatched opening bracket, return ('opening', numtabs, offset)
+ where numtabs and offset point to position one after unmatched
+ bracket.
+ * If there are no unmatched brackets, return
+ ('none', concatenatedLineText, startLineNum, endLineNum), where
+ concatenatedLineText is the resulting concatenated line, startLineNum
+ and endLineNum are the line numbers of the start and end of this line
+ respectively.
+
+ If there are unmatched closing brackets, concatenate line with
+ previous lines until there are unmatched opening brackets or no
+ unmatched brackets and return accordingly.
+ """
+ if endLineNum == -1:
+ endLineNum = startLineNum
+ umbType, umbPos = self.__findUnmatchedBrackets(line, startLineNum)
+ if umbType == 'opening':
+ print 'HNDL UNMATCHED BRACKS 1: umbType =', umbType
+ numtabs = (umbPos + 1) / self.tabwidth
+ offset = (umbPos + 1) % self.tabwidth
+ returnStmnt = 'opening', numtabs, offset
+ elif umbType == 'closing':
+ linePos = self.window.PositionFromLine(startLineNum)
+ print 'HNDL UNMATCHED BRACKS 2: umb (line, pos, char) =', linePos,
umbPos, chr(self.window.GetCharAt(linePos + umbPos))
+ mbPos = self.window.BraceMatch(linePos + umbPos)
+ print 'HNDL UNMATCHED BRACKS 3: mb (pos, char) =', mbPos,
chr(self.window.GetCharAt(mbPos))
+ if mbPos > -1:
+ mbLineNum = self.window.LineFromPosition(mbPos)
+ lines = ''
+ for i in range(mbLineNum, startLineNum):
+ l = self.window.GetLine(i)
+ lines += l
+ lines = lines + line
+ returnStmnt = self.__handleUnmatchedBrackets(lines, mbLineNum,
+ endLineNum)
+ else:
+ # No matching opening bracket found!!!

+ # For now, pretend that the line contains matching brackets.
+ # XXX Fix this -- what should it do?
+ returnStmnt = 'none', line, startLineNum, endLineNum
+ elif umbType == 'none':
+ returnStmnt = 'none', line, startLineNum, endLineNum
+ return returnStmnt
+
+
+ def __findUnmatchedBrackets(self, line, startLineNum):
+ """Search 'line' for type and position of unmatched brackets. Return
(type, position).
+
+ type
+ One of 'opening', 'closing' or 'none'.
+ position
+ For opening brackets, position in string of the last unmatched bracket.
+ For closing brackets, position in string of the first unmatched
bracket.
+ If there are no unmatched brackets, -1.
+ """
+ print 'FND UNMTCHD BRCKTS 1: line =', line
+ lineStartPos = self.window.PositionFromLine(startLineNum)
+ brackets = {'(': [], ')': [], '{': [], '}': [], '[': [], ']': []}
+ for b in brackets.keys():
+ bpos = -1
+ for i in range(line.count(b)):
+ bpos = line.find(b, bpos + 1)
+ if bpos > -1:
+ print 'FND UNMTCHD BRCKTS 2: found bracket',
chr(self.window.GetCharAt(lineStartPos + bpos))
+ if bpos > -1 and self.window.GetStyleAt(lineStartPos + bpos) == \
+ wx.stc.STC_P_OPERATOR:
+ brackets[b].append(bpos)
+ print brackets
+ lastOpenBracket = []
+ firstCloseBracket = []
+ openBrackets = ['(', '{', '[']
+ closeBrackets = [')', '}', ']']
+ for i in range(3):
+ ob = openBrackets[i]
+ cb = closeBrackets[i]
+ numOpenBrackets = len(brackets[ob])
+ numClosedBrackets = len(brackets[cb])
+ if numOpenBrackets > numClosedBrackets:
+ lastOpenBracket.append(brackets[ob][-1-numClosedBrackets])
+ elif numOpenBrackets < numClosedBrackets:
+ firstCloseBracket.append(brackets[cb][-1-numOpenBrackets])
+ if lastOpenBracket:
+ lastOpenBracket.sort()
+ print 'FND UNMTCHD BRCKTS 3: lastOpenBracket', lastOpenBracket
+ return 'opening', lastOpenBracket[-1]
+ if firstCloseBracket:
+ firstCloseBracket.sort()
+ print 'FND UNMTCHD BRCKTS 4: firstCloseBracket', firstCloseBracket
+ return 'closing', firstCloseBracket[0]
+ print 'FND UNMTCHD BRCKTS 5: brackets match'
+ return 'none', -1
+
+
+ def __handleBackslashIndent(self, line, startLineNum, endLineNum):
+ """
+ Return line concatenated with previous \-continuation lines.
+ """
+ # Check if line prior to the one we have just left is a continuation line.
+ prevPrevLine = self.window.GetLine(startLineNum - 1)
+ print 'HNDL BCKSLSH INDNT 1: line=', line
+ print 'HNDL BCKSLSH INDNT 2: prevPrevLine =', prevPrevLine
+ try:
+ lastChar = prevPrevLine[-len(self.eolMarker) - 1]
+ print 'HNDL BCKSLSH INDNT 3: lastCharPrevLine =', lastChar
+ except IndexError:
+ return line, startLineNum, endLineNum
+ else:
+ lastCharPos = self.window.PositionFromLine(startLineNum) -
len(self.eolMarker) - 1
+ print 'HNDL BCKSLSH INDNT 4: lastCharPos =', lastCharPos
+ print 'HNDL BCKSLSH INDNT 5: lastChar = ' +
chr(self.window.GetCharAt(lastCharPos))
+ if lastChar == '\\' and self.window.GetStyleAt(lastCharPos) == \
+ wx.stc.STC_P_DEFAULT:
+ line = prevPrevLine + line
+ return self.__handleBackslashIndent(line, startLineNum - 1,
+ endLineNum)
+ else:
+ return line, startLineNum, endLineNum
+
+
+ def __indentLine(self, line, startLineNum, endLineNum):
+ """Indents the new line.
+ """
+ print 'INDNT LINE 1: line =', line
+ indentLength = self.window.GetLineIndentation(startLineNum)
+ numtabs = indentLength / self.tabwidth
+ offset = indentLength % self.tabwidth
+ print 'INDNT LINE 1.5: numtabs =', numtabs
+ # Check whether the line we have just left is a continuation line.
+ try:
+ lastChar = line[-len(self.eolMarker) - 1]
+ print 'INDNT LINE 2: lastChar =', lastChar
+ except IndexError:
+ pass
+ else:
+ lastCharPos = self.window.GetLineEndPosition(endLineNum) - 1
+ print 'INDNT LINE 3: lastCharPos =', lastCharPos
+ print 'INDNT LINE 4: lastChar =',
chr(self.window.GetCharAt(lastCharPos))
+ print 'INDNT LINE 5: lastCharStyle =',
self.window.GetStyleAt(lastCharPos)
+ if lastChar == '\\' and self.window.GetStyleAt(lastCharPos) == \
+ wx.stc.STC_P_DEFAULT:
+ # XXX The following works well for lines such as
+ # if age == 27 and ... \
+ # but it doesn't work so well for lines such as
+ # var = var1 + var2 + ... + \
+ indent = self.reContinuationIndent.match(line).end()
+ numtabs = indent / self.tabwidth
+ offset = indent % self.tabwidth
+ indent = self.tabChar * numtabs + ' ' * offset
+ self.window.AddText(indent)
+ print 'INDNT LINE 5.4: added', numtabs, 'tabs + offset of', offset
+ return
+
+ # The line we have just left is not a continuation line.
+ # Check to see if it ended with a `:'.
+ lineJustLeft = line.splitlines()[-1]
+ commentPos = -1
+ for i in range(lineJustLeft.count('#')):
+ commentPos = lineJustLeft.find('#', commentPos+1)
+ print "INDT LINE 5.5: '#' found at ", commentPos
+ style =
self.window.GetStyleAt(self.window.PositionFromLine(endLineNum) +
+ commentPos)
+ print "INDT LINE 5.6: style =", style
+ if style == wx.stc.STC_P_COMMENTLINE or style ==
wx.stc.STC_P_COMMENTBLOCK:
+ lineJustLeft = lineJustLeft[:commentPos]
+ break
+ try:
+ lastChar = lineJustLeft.rstrip()[-1]
+ print 'INDNT LINE 6: lastChar.rstrip() =', lastChar
+ except IndexError:
+ pass
+ else:
+ if lastChar == ':':
+ numtabs += 1
+ self.window.AddText(self.tabChar * numtabs)
+ print 'INDNT LINE 6.5: added', numtabs, 'tabs'
+ return
+
+ # The line we have just left doesn't end with `:'.
+ # Check to see if it contains a dedenting keyword, e.g. `return'
+ mo = self.reContainsKeyword.search(line)
+ if mo and
self.window.GetStyleAt(self.window.PositionFromLine(startLineNum) +
+ mo.start()) == wx.stc.STC_P_WORD:
+ print 'INDNT LINE 7: keyword =', mo.group()
+ # Check whether line is similar to 'if x == 0: pass'.
+ justBeforeKeyword = line[:mo.start()].rstrip()
+ try:
+ lastChar = justBeforeKeyword[-1]
+ except IndexError:
+ self.window.AddText(self.tabChar * (numtabs - 1))
+ return
+ else:
+ if lastChar != ':':
+ numtabs -= 1
+ self.window.AddText(self.tabChar * numtabs)
+ print 'INDNT LINE 7.5: added', numtabs, 'tabs'
+ return
+
+ # There is nothing special about the line we have just left. Indent
+ # the new line the same as the previous line.
+ self.window.AddText(self.tabChar * numtabs + ' ' * offset)
+ print 'INDNT LINE 8: added', numtabs, 'tabs + offset of', offset
+

     def autoCompleteShow(self, command):
         """Display auto-completion popup list."""
Index: editwindow.py

RCS file: /pack/cvsroots/wxwidgets/wxWidgets/wxPython/wx/py/editwindow.py,v
retrieving revision 1.10
diff -u -r1.10 editwindow.py
--- editwindow.py 2004/03/26 19:26:34 1.10
+++ editwindow.py 2005/08/30 20:32:33
@@ -104,6 +104,8 @@
         self.SetViewWhiteSpace(False)
         self.SetTabWidth(4)
         self.SetUseTabs(False)
+ self.SetBackSpaceUnIndents(True)
+ self.SetTabIndents(True)
         # Do we want to automatically pop up command completion options?
         self.autoComplete = True
         self.autoCompleteIncludeMagic = True
Index: shell.py

RCS file: /pack/cvsroots/wxwidgets/wxWidgets/wxPython/wx/py/shell.py,v
retrieving revision 1.9
diff -u -r1.9 shell.py
--- shell.py 2004/10/15 20:31:23 1.9
+++ shell.py 2005/08/30 20:32:35
@@ -15,6 +15,7 @@
import os
import sys
import time
+import re

from buffer import Buffer
import dispatcher
@@ -236,7 +237,21 @@
         # environment. They can override anything they want.
         self.execStartupScript(self.interp.startupScript)
         wx.CallAfter(self.ScrollToLine, 0)
-
+ # Used for autoindenting
+ self.tabwidth = 4
+ self.tabChar = ' ' * self.tabwidth
+ self.reContainsKeyword = re.compile(r'(\breturn\b)|(\bbreak\b)|'
+ r'(\bpass\b)|(\bcontinue\b)|(\braise\b)')
+ self.reContinuationIndent = re.compile(r'^\s*\S*\s*')
+ eol = self.GetEOLMode()
+ if eol == wx.stc.STC_EOL_CR:
+ self.eolMarker = '\r'
+ elif eol == wx.stc.STC_EOL_CRLF:
+ self.eolMarker = '\r\n'
+ else:
+ self.eolMarker = '\n'
+ del eol
+
     def destroy(self):
         del self.interp

@@ -732,11 +747,262 @@
             self.promptPosEnd = self.GetCurrentPos()
             # Keep the undo feature from undoing previous responses.
             self.EmptyUndoBuffer()
- # XXX Add some autoindent magic here if more.
         if self.more:
- self.write(' '*4) # Temporary hack indentation.
+ self.__autoIndent()
         self.EnsureCaretVisible()
         self.ScrollToColumn(0)
+
+ def __autoIndent(self):
+ """Automatically indent or dedent the line."""
+
+ # The algorithm for indenting lines is as follows:
+ #
+ # Check whether the brackets of the line just left are paired:
+ # * Unmatched open brackets
+ # - Find last open bracket
+ # - Indent new line to one space after last open bracket
+ # * Unmatched closed brackets
+ # - Find first unmatched closed bracket
+ # - Find matching opening bracket
+ # - Concatenate intervening lines
+ # - Start indenting this new line from the begining
+ # * No unmatched brackets
+ # - If line above one just left is a \-continuation line
+ # + remove `\'
+ # + concatenate lines
+ # + restart indenting from begining
+ # - elif line just left is \-continuation line
+ # + indent to start of second word
+ # - elif line ends with `:'
+ # + increase indent level
+ # - elif line contains dedenting keyword (e.g. `return')
+ # + if character prior to keyword is `:'
+ # * pass
+ # + else
+ # * decrease indentation level
+
+ print '----------'
+ prevLineNum = self.GetCurrentLine() - 1
+ prevLine = self.lstripPrompt(self.GetLine(prevLineNum))
+ umb = self.__handleUnmatchedBrackets(prevLine, prevLineNum)
+ if umb[0] == 'opening':
+ print 'AUTO INDENT 1: umb =', umb
+ self.AddText(self.tabChar * umb[1] + ' ' * umb[2])
+ else:
+ print 'AUTO INDENT 2: umb =', umb
+ line, startLineNum, endLineNum =
self.__handleBackslashIndent(umb[1], umb[2], umb[3])
+ self.__indentLine(line, startLineNum, endLineNum)
+
+ def __handleUnmatchedBrackets(self, line, startLineNum, endLineNum=-1):
+ """
+ Return information required to indent implied continuation lines.
+
+ Return type depends on type of unmatched bracket found.
+
+ * If are unmatched opening bracket, return ('opening', numtabs, offset)
+ where numtabs and offset point to position one after unmatched
+ bracket.
+ * If there are no unmatched brackets, return
+ ('none', concatenatedLineText, startLineNum, endLineNum), where
+ concatenatedLineText is the resulting concatenated line, startLineNum
+ and endLineNum are the line numbers of the start and end of this line
+ respectively.
+
+ If there are unmatched closing brackets, concatenate line with
+ previous lines until there are unmatched opening brackets or no
+ unmatched brackets and return accordingly.
+ """
+ if endLineNum == -1:
+ endLineNum = startLineNum
+ umbType, umbPos = self.__findUnmatchedBrackets(line, startLineNum,
endLineNum)
+ if umbType == 'opening':
+ print 'HNDL UNMATCHED BRACKS 1: umbType =', umbType
+ numtabs = (umbPos + 1) / self.tabwidth
+ offset = (umbPos + 1) % self.tabwidth
+ returnStmnt = 'opening', numtabs, offset
+ elif umbType == 'closing':
+ linePos = self.PositionFromLine(startLineNum)
+ print 'HNDL UNMATCHED BRACKS 2: umb (line, pos, char) =', linePos,
umbPos, chr(self.GetCharAt(linePos + umbPos))
+ mbPos = self.BraceMatch(linePos + umbPos)
+ print 'HNDL UNMATCHED BRACKS 3: mb (pos, char) =', mbPos,
chr(self.GetCharAt(mbPos))
+ if mbPos > -1:
+ mbLineNum = self.LineFromPosition(mbPos)
+ lines = ''
+ for i in range(mbLineNum, startLineNum):
+ l = self.lstripPrompt(self.GetLine(i))
+ lines += l
+ lines = lines + line
+ returnStmnt = self.__handleUnmatchedBrackets(lines, mbLineNum,
+ endLineNum)
+ else:
+ # No matching opening bracket found!!!
+ # For now, pretend that the line contains matching brackets.
+ # XXX Fix this -- what should it do?
+ returnStmnt = 'none', line, startLineNum, endLineNum
+ elif umbType == 'none':
+ returnStmnt = 'none', line, startLineNum, endLineNum
+ return returnStmnt
+
+
+ def __findUnmatchedBrackets(self, line, startLineNum, endLineNum):
+ """Search 'line' for type and position of unmatched brackets. Return
(type, position).
+
+ type
+ One of 'opening', 'closing' or 'none'.
+ position
+ For opening brackets, position in string of the last unmatched bracket.
+ For closing brackets, position in string of the first unmatched
bracket.
+ If there are no unmatched brackets, -1.
+ """
+ print 'FND UNMTCHD BRCKTS 1: line =', line
+ lineStartPos = self.PositionFromLine(startLineNum)
+ brackets = {'(': [], ')': [], '{': [], '}': [], '[': [], ']': []}
+ for b in brackets.keys():
+ bpos = -1
+ for i in range(line.count(b)):
+ bpos = line.find(b, bpos + 1)
+ if bpos > -1:
+ print 'FND UNMTCHD BRCKTS 2: found bracket',
chr(self.GetCharAt(lineStartPos + bpos + len(line[:bpos].splitlines()) * 4 ))
+ if bpos > -1 and self.GetStyleAt(lineStartPos + bpos +
len(line[:bpos].splitlines()) * 4) == \
+ wx.stc.STC_P_OPERATOR:
+ brackets[b].append(bpos)
+ print brackets
+ lastOpenBracket = []
+ firstCloseBracket = []
+ openBrackets = ['(', '{', '[']
+ closeBrackets = [')', '}', ']']
+ for i in range(3):
+ ob = openBrackets[i]
+ cb = closeBrackets[i]
+ numOpenBrackets = len(brackets[ob])
+ numClosedBrackets = len(brackets[cb])
+ if numOpenBrackets > numClosedBrackets:
+ lastOpenBracket.append(brackets[ob][-1-numClosedBrackets])
+ elif numOpenBrackets < numClosedBrackets:
+ firstCloseBracket.append(brackets[cb][-1-numOpenBrackets])
+ if lastOpenBracket:
+ lastOpenBracket.sort()
+ print 'FND UNMTCHD BRCKTS 3: lastOpenBracket', lastOpenBracket
+ return 'opening', lastOpenBracket[-1]
+ if firstCloseBracket:
+ firstCloseBracket.sort()
+ print 'FND UNMTCHD BRCKTS 4: firstCloseBracket', firstCloseBracket
+ numPromptsRemoved = len(line[:firstCloseBracket[0]].splitlines())
+ return 'closing', firstCloseBracket[0] + numPromptsRemoved * 4
+ print 'FND UNMTCHD BRCKTS 5: brackets match'
+ return 'none', -1
+
+
+ def __handleBackslashIndent(self, line, startLineNum, endLineNum):
+ """
+ Return line concatenated with previous \-continuation lines.
+ """
+ # Check if line prior to the one we have just left is a continuation line.
+ prevPrevLine = self.lstripPrompt(self.GetLine(startLineNum - 1))
+ print 'HNDL BCKSLSH INDNT 1: line=', line
+ print 'HNDL BCKSLSH INDNT 2: prevPrevLine =', prevPrevLine
+ try:
+ lastChar = prevPrevLine[-len(self.eolMarker) - 1]
+ print 'HNDL BCKSLSH INDNT 3: lastCharPrevLine =', lastChar
+ except IndexError:
+ return line, startLineNum, endLineNum
+ else:
+ lastCharPos = self.PositionFromLine(startLineNum) -
len(self.eolMarker) - 1
+ print 'HNDL BCKSLSH INDNT 4: lastCharPos =', lastCharPos
+ print 'HNDL BCKSLSH INDNT 5: lastChar = ' +
chr(self.GetCharAt(lastCharPos))
+ if lastChar == '\\' and self.GetStyleAt(lastCharPos) == \
+ wx.stc.STC_P_DEFAULT:
+ line = prevPrevLine + line
+ return self.__handleBackslashIndent(line, startLineNum - 1,
+ endLineNum)
+ else:
+ return line, startLineNum, endLineNum
+
+
+ def __indentLine(self, line, startLineNum, endLineNum):
+ """Indents the new line.
+ """
+ print 'INDNT LINE 1: line =', line
+ indentLength = len(line) - len(line.lstrip())
+ numtabs = indentLength / self.tabwidth
+ offset = indentLength % self.tabwidth
+ print 'INDNT LINE 1.5: numtabs =', numtabs
+ # Check whether the line we have just left is a continuation line.
+ try:
+ lastChar = line[-len(self.eolMarker) - 1]
+ print 'INDNT LINE 2: lastChar =', lastChar
+ except IndexError:
+ pass
+ else:
+ lastCharPos = self.GetLineEndPosition(endLineNum) - 1
+ print 'INDNT LINE 3: lastCharPos =', lastCharPos
+ print 'INDNT LINE 4: lastChar =', chr(self.GetCharAt(lastCharPos))
+ print 'INDNT LINE 5: lastCharStyle =', self.GetStyleAt(lastCharPos)
+ if lastChar == '\\' and self.GetStyleAt(lastCharPos) == \
+ wx.stc.STC_P_DEFAULT:
+ # XXX The following works well for lines such as
+ # if age == 27 and ... \
+ # but it doesn't work so well for lines such as
+ # var = var1 + var2 + ... + \
+ indent = self.reContinuationIndent.match(line).end()
+ numtabs = indent / self.tabwidth
+ offset = indent % self.tabwidth
+ indent = self.tabChar * numtabs + ' ' * offset
+ self.AddText(indent)
+ print 'INDNT LINE 5.4: added', numtabs, 'tabs + offset of', offset
+ return
+
+ # The line we have just left is not a continuation line.
+ # Check to see if it ended with a `:'.
+ lineJustLeft = line.splitlines()[-1]
+ commentPos = -1
+ for i in range(lineJustLeft.count('#')):
+ commentPos = lineJustLeft.find('#', commentPos+1)
+ print "INDT LINE 5.5: '#' found at ", commentPos
+ style = self.GetStyleAt(self.PositionFromLine(endLineNum) +
+ commentPos + 4) # Adjust to take prompt
into account
+ print "INDT LINE 5.6: style =", style
+ if style == wx.stc.STC_P_COMMENTLINE or style ==
wx.stc.STC_P_COMMENTBLOCK:
+ lineJustLeft = lineJustLeft[:commentPos]
+ break
+ try:
+ lastChar = lineJustLeft.rstrip()[-1]
+ print 'INDNT LINE 6: lastChar.rstrip() =', lastChar
+ except IndexError:
+ pass
+ else:
+ if lastChar == ':':
+ numtabs += 1
+ self.AddText(self.tabChar * numtabs)
+ print 'INDNT LINE 6.5: added', numtabs, 'tabs'
+ return
+
+ # The line we have just left doesn't end with `:'.
+ # Check to see if it contains a dedenting keyword, e.g. `return'
+ mo = self.reContainsKeyword.search(line)
+ if mo:
+ numPromptsRemoved = len(line[:mo.start()].splitlines())
+ if self.GetStyleAt(self.PositionFromLine(startLineNum) +
+ mo.start() + numPromptsRemoved * 4) ==
wx.stc.STC_P_WORD:
+ print 'INDNT LINE 7: keyword =', mo.group()
+ # Check whether line is similar to 'if x == 0: pass'.
+ justBeforeKeyword = line[:mo.start()].rstrip()
+ try:
+ lastChar = justBeforeKeyword[-1]
+ except IndexError:
+ self.AddText(self.tabChar * (numtabs - 1))
+ return
+ else:
+ if lastChar != ':':
+ numtabs -= 1
+ self.AddText(self.tabChar * numtabs)
+ print 'INDNT LINE 7.5: added', numtabs, 'tabs'
+ return
+
+ # There is nothing special about the line we have just left. Indent
+ # the new line the same as the previous line.
+ self.AddText(self.tabChar * numtabs + ' ' * offset)
+ print 'INDNT LINE 8: added', numtabs, 'tabs + offset of', offset

     def readline(self):
         """Replacement for stdin.readline()."""

----------------------------------------------
This mail sent through http://www.ukonline.net

Hello Ben,

interesting, I would try it out, if I had more time.

Could you also simply upload your changed files as zip,
if it makes not to much trouble?

(I do not know, how to merge them);
or could you give an instruction?

What is the main difference?
(in shell, there is an autoindent mechanism)

Or could you provide two small screenshots?
Original Pycrust Indentation and your changed one
to see the result?

Cheers,

···

On Wed, 31 Aug 2005 00:26:52 +0100, Ben Armston <ben.armston@ukonline.co.uk> wrote:

Hi all,

This patch adds auto indentation to the Shell and Editor classes in Py. It
needs some polishing; there is a lot of duplicated code across the Shell and
Editor functions and too many dugging print statements, but I would like to
receive some comments on what I have done so far.

Thanks,

Ben Armston

--
Franz Steinhaeusler

Quoting Franz Steinhäusler <franz.steinhaeusler@gmx.at>:

>Hi all,
>
>This patch adds auto indentation to the Shell and Editor classes in Py. It
>needs some polishing; there is a lot of duplicated code across the Shell
>and
>Editor functions and too many dugging print statements, but I would like to
>receive some comments on what I have done so far.
>
>Thanks,
>
>Ben Armston

Hello Ben,

interesting, I would try it out, if I had more time.

Could you also simply upload your changed files as zip,
if it makes not to much trouble?

I have just discovered a couple of bugs. Hopefully I will get time to fix them
this weekend and then I will upload the zip files.

(I do not know, how to merge them);
or could you give an instruction?

To apply the diff you will need to save everything in my post starting from the
line "Index: editor.py" to the line """Replacement for stdin.readline().""" I
will assume you called it auto-indentation.diff and saved it in the
$WXDIR/wxPython/wx/py directory. This file can then be applied by a program
called patch. If you are using a Free Unix, such as Linux, you should already
have this installed. To use patch type the following at the prompt:

# cd $WXDIR/wxPython/wx/py
# patch < auto-indentation.diff

You will need to change $WXDIR to the path of the wxWidgets sources.

If you use Windows, you can still apply the patch but would have to install the
patch program first. It has been so long since I've used Windows that I can't
offer much advice on how to do this. Visiting http://www.cygwin.com/ would be a
start though.

What is the main difference?
(in shell, there is an autoindent mechanism)

The autoindentation mechanism in shell is *very* basic, mine is a lot more
sophisticated. As an example, it will indent the following in PyCrust without
the need for the tab key or backspace key to be pressed.

class Rectangle(Blob):
    def __init__(self, width, height,
                 colour='black', emphasis=None, highlight=100):
        if width == 0 and height == 0 and \
           colour == 'red' and emphasis == 'strong' or \
           highlight > 100:
            raise ValueError, "Sorry, you lose"
        if width == 0 and height == 0 and (colour == 'red' or
                                           emphasis is None or
                                           highlight < 100):
            raise ValueError, "I don't think so!"
        Blob.__init__(self, width, height,
                      colour, emphasis, highlight)

Cheers,

Franz Steinhaeusler

Thanks for the response, and I hope you get chance to test it out once I've
uploaded the zip files.

Ben Armston

···

On Wed, 31 Aug 2005 00:26:52 +0100, Ben Armston <ben.armston@ukonline.co.uk> > wrote:

----------------------------------------------
This mail sent through http://www.ukonline.net

Quoting Franz Steinhäusler <franz.steinhaeusler@gmx.at>:

>Hi all,
>
>This patch adds auto indentation to the Shell and Editor classes in Py. It
>needs some polishing; there is a lot of duplicated code across the Shell
>and
>Editor functions and too many dugging print statements, but I would like to
>receive some comments on what I have done so far.
>
>Thanks,
>
>Ben Armston

Hello Ben,

interesting, I would try it out, if I had more time.

Could you also simply upload your changed files as zip,
if it makes not to much trouble?

I have just discovered a couple of bugs. Hopefully I will get time to fix them
this weekend and then I will upload the zip files.

(I do not know, how to merge them);
or could you give an instruction?

To apply the diff you will need to save everything in my post starting from the
line "Index: editor.py" to the line """Replacement for stdin.readline().""" I
will assume you called it auto-indentation.diff and saved it in the
$WXDIR/wxPython/wx/py directory. This file can then be applied by a program
called patch. If you are using a Free Unix, such as Linux, you should already
have this installed. To use patch type the following at the prompt:

# cd $WXDIR/wxPython/wx/py
# patch < auto-indentation.diff

Hello Ben,

You will need to change $WXDIR to the path of the wxWidgets sources.

If you use Windows, you can still apply the patch but would have to install the
patch program first. It has been so long since I've used Windows that I can't
offer much advice on how to do this. Visiting http://www.cygwin.com/ would be a
start though.

What is the main difference?
(in shell, there is an autoindent mechanism)

many thanks for your information, I got it to run under windows.

The autoindentation mechanism in shell is *very* basic, mine is a lot more
sophisticated. As an example, it will indent the following in PyCrust without
the need for the tab key or backspace key to be pressed.

class Rectangle(Blob):
   def __init__(self, width, height,
                colour='black', emphasis=None, highlight=100):
       if width == 0 and height == 0 and \
          colour == 'red' and emphasis == 'strong' or \
          highlight > 100:
           raise ValueError, "Sorry, you lose"
       if width == 0 and height == 0 and (colour == 'red' or
                                          emphasis is None or
                                          highlight < 100):
           raise ValueError, "I don't think so!"
       Blob.__init__(self, width, height,
                     colour, emphasis, highlight)

Cheers,

I tried your example out (both in shell and edit window) and I must say:

Wow, it is really fantastic.

I have also patched Pycrust in the last weeks, so I want
to merge your changes into my patched one.

(Also I'll give you credits in my readme.txt file :))

Franz Steinhaeusler

Thanks for the response, and I hope you get chance to test it out once I've
uploaded the zip files.

Many thanks, this is a really useful and very good and well thought out patch!

Hopefully Robin Dunn will merge it into the wxPython distribution :wink:

Cheers,

···

On Fri, 2 Sep 2005 22:54:58 +0100, Ben Armston <ben.armston@ukonline.co.uk> wrote:

On Wed, 31 Aug 2005 00:26:52 +0100, Ben Armston <ben.armston@ukonline.co.uk> >> wrote:

--
Franz Steinhaeusler