#----------------------------------------------------------------------------- # Name: StyledTextCtrls.py # Purpose: Mixin classes to extend wx.stc.StyledTextCtrl # # Author: Riaan Booysen # # Created: 2000/04/26 # RCS-ID: $Id: StyledTextCtrls.py,v 1.36 2005/05/13 20:47:36 riaan Exp $ # Copyright: (c) 1999 - 2005 Riaan Booysen # Licence: GPL #----------------------------------------------------------------------------- import os, re, keyword, string import wx import wx.stc eols = { wx.stc.STC_EOL_CRLF : '\r\n', wx.stc.STC_EOL_CR : '\r', wx.stc.STC_EOL_LF : '\n'} import Preferences import methodparse import STCStyleEditor # from PythonWin from IDLE :) _is_block_opener = re.compile(r':\s*(#.*)?$').search _is_block_closer = re.compile(r''' \s* ( return | break | continue | raise | pass ) \b ''', re.VERBOSE).match def ver_tot(ma, mi, re): return ma*10000+mi*100+re word_delim = string.letters + string.digits + '_' object_delim = word_delim + '.' #---Utility mixins-------------------------------------------------------------- class FoldingStyledTextCtrlMix: def __init__(self, wId, margin): self.__fold_margin = margin if Preferences.edSTCFolding: self.SetProperty('fold', '1') self.SetMarginType(margin, wx.stc.STC_MARGIN_SYMBOL) self.SetMarginMask(margin, wx.stc.STC_MASK_FOLDERS) self.SetMarginSensitive(margin, True) self.SetMarginWidth(margin, Preferences.STCFoldingMarginWidth) markIdnt, markBorder, markCenter = Preferences.STCFoldingClose self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDER, markIdnt, markBorder, markCenter) self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEREND, wx.stc.STC_MARK_EMPTY, markBorder, markCenter) markIdnt, markBorder, markCenter = Preferences.STCFoldingOpen self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPEN, markIdnt, markBorder, markCenter) self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPENMID, wx.stc.STC_MARK_EMPTY, markBorder, markCenter) self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERMIDTAIL, wx.stc.STC_MARK_BACKGROUND, "white", "black") self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERSUB, wx.stc.STC_MARK_BACKGROUND, "white", "black") self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERTAIL, wx.stc.STC_MARK_BACKGROUND, "white", "black") def OnMarginClick(self, evt): # fold and unfold as needed if evt.GetMargin() == self.__fold_margin: if evt.GetShift() and evt.GetControl(): self.FoldAll() else: lineClicked = self.LineFromPosition(evt.GetPosition()) if self.GetFoldLevel(lineClicked) & wx.stc.STC_FOLDLEVELHEADERFLAG: if evt.GetShift(): self.SetFoldExpanded(lineClicked, True) self.Expand(lineClicked, True, True, 1) elif evt.GetControl(): if self.GetFoldExpanded(lineClicked): self.SetFoldExpanded(lineClicked, False) self.Expand(lineClicked, False, True, 0) else: self.SetFoldExpanded(lineClicked, True) self.Expand(lineClicked, True, True, 100) else: self.ToggleFold(lineClicked) def FoldAll(self): lineCount = self.GetLineCount() expanding = True # find out if we are folding or unfolding for lineNum in range(lineCount): if self.GetFoldLevel(lineNum) & wx.stc.STC_FOLDLEVELHEADERFLAG: expanding = not self.GetFoldExpanded(lineNum) break lineNum = 0 while lineNum < lineCount: level = self.GetFoldLevel(lineNum) if level & wx.stc.STC_FOLDLEVELHEADERFLAG and \ (level & wx.stc.STC_FOLDLEVELNUMBERMASK) == wx.stc.STC_FOLDLEVELBASE: if expanding: self.SetFoldExpanded(lineNum, True) lineNum = self.Expand(lineNum, True) lineNum = lineNum - 1 else: lastChild = self.GetLastChild(lineNum, -1) self.SetFoldExpanded(lineNum, False) if lastChild > lineNum: self.HideLines(lineNum+1, lastChild) lineNum = lineNum + 1 def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): lastChild = self.GetLastChild(line, level) line = line + 1 while line <= lastChild: if force: if visLevels > 0: self.ShowLines(line, line) else: self.HideLines(line, line) else: if doExpand: self.ShowLines(line, line) if level == -1: level = self.GetFoldLevel(line) if level & wx.stc.STC_FOLDLEVELHEADERFLAG: if force: if visLevels > 1: self.SetFoldExpanded(line, True) else: self.SetFoldExpanded(line, False) line = self.Expand(line, doExpand, force, visLevels-1) else: if doExpand and self.GetFoldExpanded(line): line = self.Expand(line, True, force, visLevels-1) else: line = self.Expand(line, False, force, visLevels-1) else: line = line + 1; return line def idWord(line, piv, lineStart, leftDelim=word_delim, rightDelim=word_delim): if piv >= len(line): return 0, 0 pivL = pivR = piv # Look left for pivL in range(piv, -1, -1): if not line[pivL] in leftDelim: pivL = pivL + 1 break # Look right for pivR in range(piv + 1, len(line)): if not line[pivR] in rightDelim: break else: pivR = pivR+1 return pivL + lineStart, pivR - pivL class BrowseStyledTextCtrlMix: """ This class is to be mix-in with a wxStyledTextCtrl to add functionality for browsing the code. """ def __init__(self, indicator=0): self.handCrs = 1 self.stndCrs = 0 self.IndicatorSetStyle(indicator, wx.stc.STC_INDIC_PLAIN) self.IndicatorSetForeground(indicator, wx.BLUE) self._indicator = indicator self.styleStart = 0 self.styleLength = 0 self.ctrlDown = False self.Bind(wx.EVT_MOTION, self.OnBrowseMotion) self.Bind(wx.EVT_LEFT_DOWN, self.OnBrowseClick) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) def doClearBrwsLn(self): self.styleStart, self.styleLength = \ self.clearUnderline(self.styleStart, self.styleLength) def BrowseClick(self, word, line, lineNo, start, style): """Called when a link is clicked. Override to use, return True if click is swallowed """ return False def StyleVeto(self, style): return False # XXX Setting the cursor irrevocably changes the cursor for the whole STC def underlineWord(self, start, length): #self.SetCursor(self.handCrs) self.SetLexer(wx.stc.STC_LEX_NULL) self.StartStyling(start, wx.stc.STC_INDICS_MASK) self.SetStyling(length, wx.stc.STC_INDIC0_MASK) return start, length def clearUnderline(self, start, length): #self.SetCursor(self.stndCrs) self.StartStyling(start, wx.stc.STC_INDICS_MASK) self.SetStyling(length, 0) self.SetLexer(wx.stc.STC_LEX_PYTHON) self.Refresh(False) return 0, 0 def getBrowsableText(self, line, piv, lnStPs): return idWord(line, piv, lnStPs) def OnBrowseMotion(self, event): event.Skip() #check if words should be underlined if event.ControlDown(): mp = event.GetPosition() pos = self.PositionFromPoint(wx.Point(mp.x, mp.y)) stl = self.GetStyleAt(pos) & 31 if self.StyleVeto(stl): if self.styleLength > 0: self.styleStart, self.styleLength = \ self.clearUnderline(self.styleStart, self.styleLength) return lnNo = self.LineFromPosition(pos) lnStPs = self.PositionFromLine(lnNo) line = self.GetLine(lnNo) piv = pos - lnStPs start, length = self.getBrowsableText(line, piv, lnStPs) #mark new if length > 0 and self.styleStart != start: if self.styleLength > 0: self.clearUnderline(self.styleStart, self.styleLength) self.styleStart,self.styleLength = \ self.underlineWord(start, length) #keep current elif self.styleStart == start: pass #delete previous elif self.styleLength > 0: self.styleStart, self.styleLength = \ self.clearUnderline(self.styleStart, self.styleLength) #clear any underlined words elif self.styleLength > 0: self.styleStart, self.styleLength = \ self.clearUnderline(self.styleStart, self.styleLength) def getStyledWordElems(self, styleStart, styleLength): if styleLength > 0: lnNo = self.LineFromPosition(styleStart) lnStPs = self.PositionFromLine(lnNo) line = self.GetLine(lnNo) start = styleStart - lnStPs word = line[start:start+styleLength] return word, line, lnNo, start else: return '', 0, 0, 0 def OnBrowseClick(self, event): word, line, lnNo, start = self.getStyledWordElems(self.styleStart, self.styleLength) if word: style = self.GetStyleAt(self.styleStart) & 31 if self.BrowseClick(word, line, lnNo, start, style): return event.Skip() def OnKeyDown(self, event): if event.ControlDown(): self.ctrlDown = True event.Skip() def OnKeyUp(self, event): if self.ctrlDown and (not event.ControlDown()): self.ctrlDown = False if self.styleLength > 0: self.styleStart, self.styleLength = \ self.clearUnderline(self.styleStart, self.styleLength) event.Skip() class CodeHelpStyledTextCtrlMix: def getCurrLineInfo(self): pos = self.GetCurrentPos() lnNo = self.GetCurrentLine() lnStPs = self.PositionFromLine(lnNo) return (pos, lnNo, lnStPs, self.GetCurLine()[0], pos - lnStPs - 1) def getFirstContinousBlock(self, docs): docs = docs.strip() res = [] for line in docs.split('\n'): if line.strip(): res.append(line) else: break return '\n'.join(res) class AutoCompleteCodeHelpSTCMix(CodeHelpStyledTextCtrlMix): """ Mixin that assists with code completion Users should implement: def getCodeCompOptions(self, word, rootWord, matchWord, lnNo): return ['list', 'of', 'options'] """ def __init__(self): self.AutoCompSetIgnoreCase(True) self.AutoCompSetCancelAtStart(False) def codeCompCheck(self): pos, lnNo, lnStPs, line, piv = self.getCurrLineInfo() start, length = idWord(line, piv, lnStPs, object_delim, object_delim) startLine = start-lnStPs word = line[startLine:startLine+length] pivword = piv - startLine dot = word.rfind('.', 0, pivword+1) matchWord = word if dot != -1: rdot = word.find('.', pivword) if rdot != -1: matchWord = word[dot+1:rdot] else: matchWord = word[dot+1:] offset = pivword - dot rootWord = word[:dot] else: offset = pivword + 1 rootWord = '' if not matchWord: offset = 0 names = self.getCodeCompOptions(word, rootWord, matchWord, lnNo) # remove duplicates and sort unqNms = {} for name in names: unqNms[name] = None names = unqNms.keys() sortnames = [(name.upper(), name) for name in names] sortnames.sort() names = [n[1] for n in sortnames] # move _* names to the end of the list cnt = 0 maxmoves = len(names) while cnt < maxmoves: if names[0] and names[0][0] == '_': names.append(names[0]) del names[0] cnt = cnt + 1 else: break if names: self.AutoCompShow(offset, ' '.join(names)) #self.AutoCompSelect(matchWord) class CallTipCodeHelpSTCMix(CodeHelpStyledTextCtrlMix): """ Mixin that assists with code completion Users should implement: def getTipValue(word, lnNo): return 'Tip' """ def __init__(self): self.lastCallTip = '' self.lastTipHilite = (0, 0) self.CallTipSetBackground(Preferences.STCCallTipBackColour) def getTipValue(self, word, lnNo): return '' def callTipCheck(self): pos, lnNo, lnStPs, line, piv = self.getCurrLineInfo() bracket = methodparse.matchbracket(line[:piv+1], '(') if bracket == -1 and self.CallTipActive(): self.CallTipCancel() return cursBrktOffset = piv - bracket start, length = idWord(line, bracket-1, lnStPs, object_delim, object_delim) startLine = start-lnStPs word = line[startLine:startLine+length] if word: tip = self.getTipValue(word, lnNo) if tip: # Minus offset of 1st bracket in the tip tipBrkt = tip.find('(') if tipBrkt != -1: pos = pos - tipBrkt - 1 else: tipBrkt = 0 # get the current parameter from source paramNo = len(methodparse.safesplitfields(\ line[bracket+1:piv+1]+'X', ',')) if paramNo: paramNo = paramNo - 1 # get hilight & corresponding parameter from tip tipBrktEnd = tip.rfind(')') tip_param_str = tip[tipBrkt+1:tipBrktEnd] tip_params = methodparse.safesplitfields(\ tip_param_str, ',', ('(', '{'), (')', '}') ) try: hiliteStart = tipBrkt+1 + tip_param_str.find(tip_params[paramNo]) except IndexError: hilite = (0, 0) else: hilite = (hiliteStart, hiliteStart+len(tip_params[paramNo])) # don't update if active and unchanged if self.CallTipActive() and tip == self.lastCallTip and \ hilite == self.lastTipHilite: return # close if active and changed if self.CallTipActive() and (tip != self.lastCallTip or \ hilite != self.lastTipHilite): self.CallTipCancel() self.CallTipShow(pos - cursBrktOffset, tip) self.CallTipSetHighlight(hilite[0], hilite[1]) self.lastCallTip = tip self.lastTipHilite = hilite class DebuggingViewSTCMix: def __init__(self, debugMarkers): (self.brkPtMrk, self.tmpBrkPtMrk, self.disabledBrkPtMrk, self.stepPosMrk) = debugMarkers # XXX properly allocate the marker self.stepPosBackMrk = self.stepPosMrk + 1 # Initialize breakpoints from file and running debugger # XXX Remote breakpoints should be stored in a local pickle filename = self.model.filename from Debugger.Breakpoint import bplist self.breaks = bplist.getFileBreakpoints(filename) self.tryLoadBreakpoints() def setupDebuggingMargin(self, symbolMrg): self.SetMarginType(symbolMrg, wx.stc.STC_MARGIN_SYMBOL) self.SetMarginWidth(symbolMrg, Preferences.STCSymbolMarginWidth) self.SetMarginSensitive(symbolMrg, True) markIdnt, markBorder, markCenter = Preferences.STCBreakpointMarker self.MarkerDefine(self.brkPtMrk, markIdnt, markBorder, markCenter) markIdnt, markBorder, markCenter = Preferences.STCLinePointer self.MarkerDefine(self.stepPosMrk, markIdnt, markBorder, markCenter) markIdnt, markBorder, markCenter = Preferences.STCTmpBreakpointMarker self.MarkerDefine(self.tmpBrkPtMrk, markIdnt, markBorder, markCenter) markIdnt, markBorder, markCenter = Preferences.STCDisabledBreakpointMarker self.MarkerDefine(self.disabledBrkPtMrk, markIdnt, markBorder, markCenter) try: wx.stc.STC_MARK_BACKGROUND except: self.MarkerDefine(self.stepPosBackMrk, wx.stc.STC_MARK_EMPTY, wx.Colour(255, 255, 255), wx.Colour(128, 128, 255)) else: self.MarkerDefine(self.stepPosBackMrk, wx.stc.STC_MARK_BACKGROUND, wx.Colour(255, 255, 255), wx.Colour(220, 220, 255)) def setInitialBreakpoints(self): # Adds markers where the breakpoints are located. for mrk in (self.tmpBrkPtMrk, self.disabledBrkPtMrk, self.brkPtMrk): self.MarkerDeleteAll(mrk) for brk in self.breaks.listBreakpoints(): self.setBreakMarker(brk) def setBreakMarker(self, brk): if brk['temporary']: mrk = self.tmpBrkPtMrk elif not brk['enabled']: mrk = self.disabledBrkPtMrk else: mrk = self.brkPtMrk lineno = brk['lineno'] - 1 currMrk = self.MarkerGet(lineno) & (1 << mrk) if currMrk: self.MarkerDelete(lineno, mrk) self.MarkerAdd(lineno, mrk) def deleteBreakMarkers(self, lineNo): self.MarkerDelete(lineNo - 1, self.brkPtMrk) self.MarkerDelete(lineNo - 1, self.tmpBrkPtMrk) self.MarkerDelete(lineNo - 1, self.disabledBrkPtMrk) def deleteBreakPoint(self, lineNo): self.breaks.deleteBreakpoints(lineNo) debugger = self.model.editor.debugger if debugger: # Try to apply to the running debugger. debugger.deleteBreakpoints(self.model.filename, lineNo) self.deleteBreakMarkers(lineNo) def addBreakPoint(self, lineNo, temp=0, notify_debugger=1): filename = self.model.filename self.breaks.addBreakpoint(lineNo, temp) if notify_debugger: debugger = self.model.editor.debugger if debugger: # Try to apply to the running debugger. debugger.setBreakpoint(filename, lineNo, temp) if temp: mrk = self.tmpBrkPtMrk else: mrk = self.brkPtMrk self.MarkerAdd(lineNo - 1, mrk) def moveBreakpoint(self, bpt, delta): # remove index = (bpt.file, bpt.line) if index not in bpt.bplist.keys(): return bpt.bplist[index].remove(bpt) if not bpt.bplist[index]: del bpt.bplist[index] del self.breaks[bpt.line] bpt.line = bpt.line + delta # re-add index = (bpt.file, bpt.line) if bpt.bplist.has_key(index): bpt.bplist[index].append(bpt) else: bpt.bplist[index] = [bpt] self.breaks[bpt.line] = bpt def tryLoadBreakpoints(self): import pickle fn = self.getBreakpointFilename() if fn: rval = self.breaks.loadBreakpoints(fn) if rval: self.setInitialBreakpoints() else: rval = None return rval def saveBreakpoints(self): # XXX This is not yet called automatically on saving a module, # should it be ? fn = self.getBreakpointFilename() if fn: self.breaks.saveBreakpoints(fn) def clearStepPos(self, lineNo): if lineNo < 0: lineNo = 0 self.MarkerDelete(lineNo, self.stepPosMrk) self.MarkerDelete(lineNo, self.stepPosBackMrk) def setStepPos(self, lineNo): if lineNo < 0: lineNo = 0 self.MarkerDeleteAll(self.stepPosMrk) self.MarkerAdd(lineNo, self.stepPosMrk) self.MarkerDeleteAll(self.stepPosBackMrk) self.MarkerAdd(lineNo, self.stepPosBackMrk) if self.breaks.hasBreakpoint(lineNo + 1): # Be sure all the breakpoints for this file are displayed. self.setInitialBreakpoints() self.MarkerDelete(lineNo, self.tmpBrkPtMrk) def getBreakpointFilename(self): try: return os.path.splitext(self.model.assertLocalFile())[0]+'.brk' except AssertionError: return '' def adjustBreakpoints(self, linesAdded, modType, evtPos): line = self.LineFromPosition(evtPos)#event.GetPosition()) endline = self.GetLineCount() if self.breaks.hasBreakpoint( min(line, endline), max(line, endline)): changed = self.breaks.adjustBreakpoints(line, linesAdded) debugger = self.model.editor.debugger if debugger: # XXX also check that module has been imported # XXX this should apply; with or without breakpoint if debugger.running and \ not modType & wx.stc.STC_PERFORMED_UNDO: wx.LogWarning('Adding or removing lines from the ' 'debugger will cause the source and the ' 'debugger to be out of sync.' '\nPlease undo this action.') debugger.adjustBreakpoints(self.model.filename, line, linesAdded) else: # bdb must still be updated import bdb bpList = bdb.Breakpoint.bplist filename = self.model.filename #canonic form, same as url form I think setBreaks = [] # store reference and remove from (fn, ln) refed dict. for bpFile, bpLine in bpList.keys(): if bpFile == filename and bpLine > line: setBreaks.append(bpList[bpFile, bpLine]) del bpList[bpFile, bpLine] # put old break at new place and renumber for brks in setBreaks: for brk in brks: brk.line = brk.line + linesAdded # merge in moved breaks if bpList.has_key((filename, brk.line)): bpList[filename, brk.line].append(brk) else: bplist[filename, brk.line] = [brk] def OnSaveBreakPoints(self, event): self.saveBreakpoints() def OnLoadBreakPoints(self, event): self.tryLoadBreakpoints() def OnSetBreakPoint(self, event): line = self.LineFromPosition(self.GetCurrentPos()) + 1 if self.breaks.hasBreakpoint(line): self.deleteBreakPoint(line) else: self.addBreakPoint(line) def OnMarginClick(self, event): lineClicked = self.LineFromPosition(event.GetPosition()) + 1 if self.breaks.hasBreakpoint(lineClicked): self.deleteBreakPoint(lineClicked) else: self.addBreakPoint(lineClicked) #---Language mixins------------------------------------------------------------- class LanguageSTCMix: def __init__(self, wId, marginNumWidth, language, config): self.language = language (cfg, self.commonDefs, self.styleIdNames, self.styles, psgn, psg, olsgn, olsg, ds, self.lexer, self.keywords, bi) = \ self.getSTCStyles(config, language) #self.SetEOLMode(wx.stc.STC_EOL_LF) #self.eol = '\n' self.SetBufferedDraw(Preferences.STCBufferedDraw) self.SetCaretPeriod(Preferences.STCCaretPeriod) self.SetViewWhiteSpace(Preferences.STCViewWhiteSpace) self.SetViewEOL(Preferences.STCViewEOL) self.SetIndent(Preferences.STCIndent) self.SetUseTabs(Preferences.STCUseTabs) self.SetTabWidth(Preferences.STCTabWidth) self.SetCaretPeriod(Preferences.STCCaretPeriod) #if Preferences.STCCaretPolicy: # self.SetCaretPolicy(Preferences.STCCaretPolicy, # Preferences.STCCaretPolicySlop) self.SetEdgeMode(Preferences.STCEdgeMode) self.SetEdgeColumn(Preferences.STCEdgeColumnWidth) if marginNumWidth: self.SetMargins(1, 1) self.SetMarginType(marginNumWidth[0], wx.stc.STC_MARGIN_NUMBER) self.SetMarginWidth(marginNumWidth[0], marginNumWidth[1]) self.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnUpdateUI, id=wId) def setStyles(self, commonOverride=None): commonDefs = {} commonDefs.update(self.commonDefs) if commonOverride is not None: commonDefs.update(commonOverride) STCStyleEditor.setSTCStyles(self, self.styles, self.styleIdNames, commonDefs, self.language, self.lexer, self.keywords) def getSTCStyles(self, config, language): """ Override to set values directly """ return STCStyleEditor.initFromConfig(config, language) keymap={'euro': {226: chr(124), 69: chr(128), 81: chr(64), 77: chr(181), 48: chr(125), 337: chr(126), 50: chr(178), 51: chr(179), 55: chr(123), 56: chr(91), 57: chr(93), 219: chr(92), }, 'swiss-german': {192: chr(93), 226: chr(92), 50: chr(64), 51: chr(35), 55: chr(124), 186: chr(91), 219: chr(96), 220: chr(123), 221: chr(126), 223: chr(125), }, 'italian': {192: chr(64), 337: chr(93), 186: chr(91), 219: chr(123), 221: chr(125), 222: chr(35), }, 'france': {226: chr(54), 48: chr(64), 337: chr(125), 50: chr(126), 51: chr(35), 52: chr(123), 53: chr(91), 54: chr(124), 55: chr(96), 56: chr(92), 219: chr(93), }, } def handleSpecialEuropeanKeys(self, event, countryKeymap='euro'): key = event.KeyCode() keymap = self.keymap[countryKeymap] if event.AltDown() and event.ControlDown() and keymap.has_key(key): currPos = self.GetCurrentPos() self.InsertText(currPos, keymap[key]) self.SetCurrentPos(self.GetCurrentPos()+1) self.SetSelectionStart(self.GetCurrentPos()) stcConfigPath = os.path.join(Preferences.rcPath, 'stc-styles.rc.cfg') class PythonStyledTextCtrlMix(LanguageSTCMix): def __init__(self, wId, margin): LanguageSTCMix.__init__(self, wId, margin, 'python', stcConfigPath) self.keywords = self.keywords + ' yield None' try: True except NameError: self.keywords = self.keywords + ' True False' else: self.keywords = self.keywords + ' True False' self.setStyles() def grayout(self, do): if not Preferences.grayoutSource: return if do: f = {'backcol': '#EEF2FF'} else: f = None self.setStyles(f) def OnUpdateUI(self, event): # check for matching braces braceAtCaret = -1 braceOpposite = -1 charBefore = None caretPos = self.GetCurrentPos() if caretPos > 0: try: charBefore = chr(self.GetCharAt(caretPos - 1)) except ValueError: charBefore = '' styleBefore = self.GetStyleAt(caretPos - 1) # check before if charBefore and charBefore in "[]{}()" and styleBefore == 10: braceAtCaret = caretPos - 1 # check after if braceAtCaret < 0: try: charAfter = chr(self.GetCharAt(caretPos)) except ValueError: charAfter = '' styleAfter = self.GetStyleAt(caretPos) if charAfter and charAfter in "[]{}()" and styleAfter == 10: braceAtCaret = caretPos if braceAtCaret >= 0: braceOpposite = self.BraceMatch(braceAtCaret) if braceAtCaret != -1 and braceOpposite == -1: self.BraceBadLight(braceAtCaret) else: self.BraceHighlight(braceAtCaret, braceOpposite) # self.Refresh(False) def doAutoIndent(self, prevline, pos): stripprevline = prevline.strip() if stripprevline: indent = prevline[:prevline.find(stripprevline)] else: try: indent = prevline.strip('\r\n', 1) except TypeError: indent = prevline while indent and indent[-1] in ('\r', '\n'): indent = indent[:-1] if self.GetUseTabs(): indtBlock = '\t' else: # XXX Why did I do this? indtBlock = self.GetTabWidth()*' ' if _is_block_opener(prevline): indent = indent + indtBlock elif _is_block_closer(prevline): indent = indent[:-1*len(indtBlock)] self.BeginUndoAction() try: self.InsertText(pos, indent) self.GotoPos(pos + len(indent)) finally: self.EndUndoAction() class TextSTCMix(LanguageSTCMix): def __init__(self, wId): LanguageSTCMix.__init__(self, wId, (), 'text', stcConfigPath) self.setStyles() ## 1 : ## 2 : diff ## 3 : +++/--- ## 4 : @ ## 5 : - ## 6 : + class DiffSTCMix(LanguageSTCMix): def __init__(self, wId): LanguageSTCMix.__init__(self, wId, (), 'diff', stcConfigPath) self.setStyles() from types import IntType, SliceType, StringType class STCLinesList: def __init__(self, STC): self.__STC = STC def _rememberPos(self): self.__pos = self.GetCurrentPos() def __getitem__(self, key): if type(key) is IntType: # XXX last char is garbage if key < len(self): return self.__STC.GetLine(key) else: raise IndexError elif type(key) is SliceType: res = [] for idx in range(key.start, key.stop): res.append(self[idx]) return res else: raise TypeError, '%s not supported' % `type(key)` def __setitem__(self, key, value): stc = self.__STC if type(key) is IntType: assert type(value) is StringType if key < len(self): stc.SetSelection(stc.PositionFromLine(key), stc.GetLineEndPosition(key)) stc.ReplaceSelection(value) else: raise IndexError elif type(key) is SliceType: lines = eols[stc.GetEOLMode()].join(value) stc.SetSelection(stc.PositionFromLine(key.start), stc.GetLineEndPosition(key.stop)) stc.ReplaceSelection(lines) else: raise TypeError, '%s not supported' % `type(key)` def __delitem__(self, key): stc = self.__STC if type(key) is IntType: stc.SetSelection(stc.PositionFromLine(key), stc.GetLineEndPosition(key)+1) stc.ReplaceSelection('') elif type(key) is SliceType: stc.SetSelection(stc.PositionFromLine(key.start), stc.GetLineEndPosition(key.stop)+1) stc.ReplaceSelection('') else: raise TypeError, '%s not supported' % `type(key)` def __getattr__(self, name): if name == 'current': return self.__STC.GetCurrentLine() if name == 'count': return self.__STC.GetLineCount() if name == 'size': return self.__STC.GetTextLength() # dubious if name == 'line': return self.__STC.GetCurLine()[0] # dubious if name == 'pos': return self.__STC.GetCurrentPos() raise AttributeError, name def __len__(self): return self.__STC.GetLineCount()