| 1 | #!-*- coding:iso8859-1 -*- |
|---|
| 2 | """ |
|---|
| 3 | Este archivo es parte de Pyragua |
|---|
| 4 | |
|---|
| 5 | Pyragua es software libre; lo puedes redistribuir y/o modificar |
|---|
| 6 | bajo los terminos de la Licencia Publica General (GNU GPL) como fue |
|---|
| 7 | publicada por la Free Software Foundation; cualquier versión 2 de la |
|---|
| 8 | Licencia. |
|---|
| 9 | |
|---|
| 10 | Este programa es distribuido con la esperanza de que será útil, |
|---|
| 11 | pero SIN GARANTIA ALGUNA; ni con la garantía explícita de |
|---|
| 12 | MERCABILIDAD o de que SERVIRA PARA UN PROPOSITO EN PARTICULAR. |
|---|
| 13 | Mire la Licencia Pública General de la GNU para más detalles. |
|---|
| 14 | |
|---|
| 15 | Debió recibir una copia de la Licencia Pública General de la GNU junto con |
|---|
| 16 | este programa; sino, escriba a la Free Software Foundation, |
|---|
| 17 | Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 18 | """ |
|---|
| 19 | DEBUG=False |
|---|
| 20 | import wx.stc as stc |
|---|
| 21 | import wx |
|---|
| 22 | import keyword |
|---|
| 23 | # Para i18n |
|---|
| 24 | import gettext |
|---|
| 25 | gettext.install("pyragua",unicode=1) |
|---|
| 26 | _ = gettext.gettext |
|---|
| 27 | |
|---|
| 28 | from Utils import * |
|---|
| 29 | |
|---|
| 30 | #Este módulo sirve para buscar cuales son los módulos de una variable |
|---|
| 31 | import wx.py |
|---|
| 32 | |
|---|
| 33 | import os,sys |
|---|
| 34 | |
|---|
| 35 | if wx.Platform == '__WXMSW__': |
|---|
| 36 | faces = { 'times': 'Times New Roman', |
|---|
| 37 | 'mono' : 'Courier New', |
|---|
| 38 | 'helv' : 'Arial', |
|---|
| 39 | 'other': 'Comic Sans MS', |
|---|
| 40 | 'size' : 10, |
|---|
| 41 | 'size2': 8, |
|---|
| 42 | } |
|---|
| 43 | else: |
|---|
| 44 | faces = { 'times': 'Times', |
|---|
| 45 | 'mono' : 'Courier', |
|---|
| 46 | 'helv' : 'Helvetica', |
|---|
| 47 | 'other': 'new century schoolbook', |
|---|
| 48 | 'size' : 12, |
|---|
| 49 | 'size2': 10, |
|---|
| 50 | } |
|---|
| 51 | |
|---|
| 52 | class PythonSTC(stc.StyledTextCtrl): |
|---|
| 53 | fold_symbols = 3 |
|---|
| 54 | |
|---|
| 55 | def __init__(self, archivo, ID, |
|---|
| 56 | pos=wx.DefaultPosition, size=wx.DefaultSize, |
|---|
| 57 | style=0): |
|---|
| 58 | self.archivo=archivo |
|---|
| 59 | self.pyragua=self.archivo.pyragua |
|---|
| 60 | stc.StyledTextCtrl.__init__(self, archivo, ID, pos, size, style) |
|---|
| 61 | self.Configurar() |
|---|
| 62 | self.SetModEventMask(stc.STC_MOD_INSERTTEXT | stc.STC_MOD_DELETETEXT | stc.STC_PERFORMED_USER ) |
|---|
| 63 | #Utilizaremos este diccionario para llevar un registro del espacio |
|---|
| 64 | #de nombres de cada variable para el autocompletar |
|---|
| 65 | self.namespace={} |
|---|
| 66 | |
|---|
| 67 | def Configurar(self): |
|---|
| 68 | u"""Establece los parámetros de configuración del stc""" |
|---|
| 69 | self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) |
|---|
| 70 | self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) |
|---|
| 71 | self.CmdKeyAssign(ord('K'), stc.STC_SCMOD_CTRL, stc.STC_CMD_LINEDELETE) |
|---|
| 72 | self.CmdKeyAssign(ord('Z'), stc.STC_SCMOD_CTRL, stc.STC_CMD_UNDO) |
|---|
| 73 | |
|---|
| 74 | self.SetLexer(stc.STC_LEX_PYTHON) |
|---|
| 75 | keyword .kwlist+=['True','False','self'] |
|---|
| 76 | self.SetKeyWords(0, " ".join(keyword.kwlist)) |
|---|
| 77 | # esto sirve para resaltar estas palabras ademas de las que estan ya en keyword.kwlist |
|---|
| 78 | self.CadenaParaComentar='#_' |
|---|
| 79 | |
|---|
| 80 | self.SetProperty("fold", "1") |
|---|
| 81 | self.SetProperty("tab.timmy.whinge.level", "1") |
|---|
| 82 | self.SetMargins(0,0) |
|---|
| 83 | |
|---|
| 84 | self.SetViewWhiteSpace(False) |
|---|
| 85 | #self.SetBufferedDraw(False) |
|---|
| 86 | #self.SetViewEOL(True) |
|---|
| 87 | #self.SetEOLMode(stc.STC_EOL_CRLF) |
|---|
| 88 | #self.SetUseAntiAliasing(True) |
|---|
| 89 | self.TIPO_EOL=stc.STC_EOL_CRLF #Variable que contiene el tipo de EOL |
|---|
| 90 | '''stc.STC_EOL_CRLF |
|---|
| 91 | stc.STC_EOL_CR |
|---|
| 92 | stc.STC_EOL_LF''' |
|---|
| 93 | self.SetEOLMode(self.TIPO_EOL) |
|---|
| 94 | #self.SetEdgeMode(stc.STC_EDGE_BACKGROUND) |
|---|
| 95 | #self.SetEdgeColumn(78) |
|---|
| 96 | self.TAMESPC=4#tamaño de cada tabulacion |
|---|
| 97 | self.SetTabWidth(self.TAMESPC) |
|---|
| 98 | # Setup a margin to hold fold markers |
|---|
| 99 | #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER? |
|---|
| 100 | self.SetMarginType(2, stc.STC_MARGIN_SYMBOL) |
|---|
| 101 | self.SetMarginMask(2, stc.STC_MASK_FOLDERS) |
|---|
| 102 | self.SetMarginSensitive(2, True) |
|---|
| 103 | self.SetMarginWidth(2, 12) |
|---|
| 104 | |
|---|
| 105 | if self.fold_symbols == 0: |
|---|
| 106 | # Arrow pointing right for contracted folders, arrow pointing down for expanded |
|---|
| 107 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black") |
|---|
| 108 | self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black") |
|---|
| 109 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black") |
|---|
| 110 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black") |
|---|
| 111 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black") |
|---|
| 112 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black") |
|---|
| 113 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black") |
|---|
| 114 | |
|---|
| 115 | elif self.fold_symbols == 1: |
|---|
| 116 | # Plus for contracted folders, minus for expanded |
|---|
| 117 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black") |
|---|
| 118 | self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black") |
|---|
| 119 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black") |
|---|
| 120 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black") |
|---|
| 121 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black") |
|---|
| 122 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black") |
|---|
| 123 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black") |
|---|
| 124 | |
|---|
| 125 | elif self.fold_symbols == 2: |
|---|
| 126 | # Like a flattened tree control using circular headers and curved joins |
|---|
| 127 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040") |
|---|
| 128 | self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040") |
|---|
| 129 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040") |
|---|
| 130 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040") |
|---|
| 131 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040") |
|---|
| 132 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040") |
|---|
| 133 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040") |
|---|
| 134 | |
|---|
| 135 | elif self.fold_symbols == 3: |
|---|
| 136 | # Like a flattened tree control using square headers |
|---|
| 137 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080") |
|---|
| 138 | self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080") |
|---|
| 139 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080") |
|---|
| 140 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080") |
|---|
| 141 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080") |
|---|
| 142 | self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080") |
|---|
| 143 | self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080") |
|---|
| 144 | |
|---|
| 145 | self.Bind(stc.EVT_STC_MODIFIED,self.ChangeText) |
|---|
| 146 | self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI) |
|---|
| 147 | self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick) |
|---|
| 148 | self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed) |
|---|
| 149 | self.Bind(wx.EVT_CHAR, self.OnChar) |
|---|
| 150 | |
|---|
| 151 | # Make some styles, The lexer defines what each style is used for, we |
|---|
| 152 | # just have to define what each style looks like. This set is adapted from |
|---|
| 153 | # Scintilla sample property files. |
|---|
| 154 | |
|---|
| 155 | #Copiado del editor del demo de wxpy |
|---|
| 156 | # Indentation and tab stuff |
|---|
| 157 | self.SetIndent(self.TAMESPC) # Proscribed indent size for wx |
|---|
| 158 | self.SetIndentationGuides(True) # Show indent guides |
|---|
| 159 | self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space |
|---|
| 160 | self.SetTabIndents(True) # Tab key indents |
|---|
| 161 | #self.SetTabWidth(4) # Proscribed tab size for wx |
|---|
| 162 | self.SetUseTabs(False) # Use spaces rather than tabs, or |
|---|
| 163 | |
|---|
| 164 | # Global default styles for all languages |
|---|
| 165 | self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces) |
|---|
| 166 | self.StyleClearAll() # Reset all to be like the default |
|---|
| 167 | |
|---|
| 168 | # Global default styles for all languages |
|---|
| 169 | self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces) |
|---|
| 170 | self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces) |
|---|
| 171 | self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces) |
|---|
| 172 | self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold") |
|---|
| 173 | self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold") |
|---|
| 174 | # Indentation guide |
|---|
| 175 | self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD") |
|---|
| 176 | |
|---|
| 177 | # Python styles |
|---|
| 178 | # Default |
|---|
| 179 | self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces) |
|---|
| 180 | # Comments |
|---|
| 181 | self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces) |
|---|
| 182 | # Number |
|---|
| 183 | self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces) |
|---|
| 184 | # String |
|---|
| 185 | self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces) |
|---|
| 186 | # Single quoted string |
|---|
| 187 | self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces) |
|---|
| 188 | # Keyword |
|---|
| 189 | self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces) |
|---|
| 190 | # Triple quotes |
|---|
| 191 | self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces) |
|---|
| 192 | # Triple double quotes |
|---|
| 193 | self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces) |
|---|
| 194 | # Class name definition |
|---|
| 195 | self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces) |
|---|
| 196 | # Function or method name definition |
|---|
| 197 | self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces) |
|---|
| 198 | # Operators |
|---|
| 199 | self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces) |
|---|
| 200 | # Identifiers |
|---|
| 201 | self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces) |
|---|
| 202 | # Comment-blocks |
|---|
| 203 | self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces) |
|---|
| 204 | # End of line where string is not closed |
|---|
| 205 | self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces) |
|---|
| 206 | |
|---|
| 207 | self.SetCaretForeground("BLUE") |
|---|
| 208 | def DesHacer(self,evento): |
|---|
| 209 | if self.CanUndo()==1 : |
|---|
| 210 | self.Undo() |
|---|
| 211 | else: |
|---|
| 212 | self.DesAcusarModificacion() |
|---|
| 213 | |
|---|
| 214 | def ReHacer(self,evento): |
|---|
| 215 | if self.CanRedo() : |
|---|
| 216 | self.Redo() |
|---|
| 217 | self.AcusarModificacion() |
|---|
| 218 | |
|---|
| 219 | def OnCopy(self, evento): |
|---|
| 220 | self.Copy() |
|---|
| 221 | |
|---|
| 222 | def OnCut(self, evento): |
|---|
| 223 | self.Cut() |
|---|
| 224 | |
|---|
| 225 | def OnPaste(self, evento): |
|---|
| 226 | self.Paste() |
|---|
| 227 | |
|---|
| 228 | def Tabular(self,linea): |
|---|
| 229 | """Determina si la linea de texto debe ser tabulada y cuantos caracteres |
|---|
| 230 | extras debe poner para completar la tabulación. |
|---|
| 231 | Tabular(str) --> bool, int""" |
|---|
| 232 | band=False #Determina si la linea debe ser tabulada |
|---|
| 233 | m=0#cantidad de espacios extras a colocar en caso de ser necesario |
|---|
| 234 | ban=True |
|---|
| 235 | balp=0#balance de parantesis |
|---|
| 236 | i=0 |
|---|
| 237 | while i<len(linea):#bucle para quitar las cadenas para no confundir algun comentario |
|---|
| 238 | j=i |
|---|
| 239 | if linea[i]=='"' and not linea[i:i+3]=='"""':#para quitar las cadenas en comillas dobles |
|---|
| 240 | j=j+1 |
|---|
| 241 | while j<len(linea) and linea[j]!='"': |
|---|
| 242 | j=j+1 |
|---|
| 243 | #if j>=len(linea): |
|---|
| 244 | #print 'se salio'#se salio |
|---|
| 245 | linea= linea[:i]+linea[j+1:] |
|---|
| 246 | i=i-1 |
|---|
| 247 | elif linea[i:i+3]=='"""':#para quitar las cadenas en triple comilla doble |
|---|
| 248 | j=j+3 |
|---|
| 249 | while j<len(linea) and linea[j:j+3]!='"""': |
|---|
| 250 | j=j+1 |
|---|
| 251 | #print 'i = %d, j = %d'%(i,j) |
|---|
| 252 | #if j>=len(linea): |
|---|
| 253 | #print 'se salio'#se salio |
|---|
| 254 | linea= linea[:i]+linea[j+3:] |
|---|
| 255 | i=i-1 |
|---|
| 256 | elif linea[i]=="'" and not linea[i:i+3]=="'''":#para quitar las cadenas en comilla sencilla |
|---|
| 257 | j=j+1 |
|---|
| 258 | while j<len(linea) and linea[j]!="'": |
|---|
| 259 | j=j+1 |
|---|
| 260 | #if j>=len(linea): |
|---|
| 261 | #print 'se salio'#se salio |
|---|
| 262 | linea= linea[:i]+linea[j+1:] |
|---|
| 263 | i=i-1 |
|---|
| 264 | elif linea[i:i+3]=="'''":#tpara quitar las cadenas en riple comilla sencilla |
|---|
| 265 | j=j+3 |
|---|
| 266 | while j<len(linea) and linea[j:j+3]!="'''": |
|---|
| 267 | j=j+1 |
|---|
| 268 | #print 'i = %d, j = %d'%(i,j) |
|---|
| 269 | #if j>=len(linea): |
|---|
| 270 | #print 'se salio'#se salio |
|---|
| 271 | linea= linea[:i]+linea[j+3:] |
|---|
| 272 | i=i-1 |
|---|
| 273 | i=i+1 |
|---|
| 274 | pos=linea.find('#')#se le borran los comentarios #xxxxx |
|---|
| 275 | if pos >=0: |
|---|
| 276 | linea=linea[:pos] |
|---|
| 277 | ppal=(linea.split(' '))[0]#ppal contiene la primera palabra de la linea |
|---|
| 278 | linea=linea.strip() |
|---|
| 279 | band=linea.endswith(':') |
|---|
| 280 | i=len(linea)-1 |
|---|
| 281 | while i>=0:#este ciclo es para saber si quedo algun parentesis abierto sin cerrar al final de la linea |
|---|
| 282 | x=linea[i] |
|---|
| 283 | if x=='(': |
|---|
| 284 | balp=balp-1 |
|---|
| 285 | if x==')': |
|---|
| 286 | balp=balp+1 |
|---|
| 287 | if balp==-1: |
|---|
| 288 | band=False |
|---|
| 289 | m=i |
|---|
| 290 | break |
|---|
| 291 | i=i-1 |
|---|
| 292 | palabras=['if', 'while', 'for', 'class', 'try','else','except','def','elif']#las palabras reservadas que llevan tabulacion |
|---|
| 293 | if m >0: |
|---|
| 294 | band=False |
|---|
| 295 | bandera=False |
|---|
| 296 | for x in palabras:#para determinar si la linea comienza con una palabra correcta para indentar |
|---|
| 297 | if linea.startswith(x): |
|---|
| 298 | bandera=True |
|---|
| 299 | if not bandera and m==0: |
|---|
| 300 | band=False |
|---|
| 301 | return band,m |
|---|
| 302 | |
|---|
| 303 | def NuevaLinea ( self ): |
|---|
| 304 | """Inserta una nueva linea segun la plataforma |
|---|
| 305 | """ |
|---|
| 306 | if DEBUG : print "Nueva Línea", self.GetEOLMode() |
|---|
| 307 | if self.GetEOLMode()==stc.STC_EOL_CRLF: |
|---|
| 308 | self.AddTextRaw('\r\n') |
|---|
| 309 | elif self.GetEOLMode()==stc.STC_EOL_CR: |
|---|
| 310 | self.AddTextRaw('\r') |
|---|
| 311 | elif self.GetEOLMode()==stc.STC_EOL_LF: |
|---|
| 312 | self.AddTextRaw('\n') |
|---|
| 313 | |
|---|
| 314 | def CalcularTab( self ): |
|---|
| 315 | """Funcion que calcula si al recibir un enter debe hacer una tabulacion extra e inserta la nueva linea. |
|---|
| 316 | """ |
|---|
| 317 | ESP=' '#un caracter espacio, cuidado al modificar |
|---|
| 318 | TAB=' '#un caracter tab, cuidado al modificar |
|---|
| 319 | espacio=' '#el caracter por defecto de la tabulacion |
|---|
| 320 | m=0#cantidad de espacios extras a colocar en la tabulacion |
|---|
| 321 | numtabs=0#numero de tabulaciones |
|---|
| 322 | (linea,pos)=self.GetCurLine() |
|---|
| 323 | numtabs=self.GetLineIndentation(self.GetCurrentLine()) |
|---|
| 324 | linea=linea[:pos] |
|---|
| 325 | if pos>=numtabs: |
|---|
| 326 | if numtabs>0: |
|---|
| 327 | if len(linea)!=0 : |
|---|
| 328 | espacio=linea[0] |
|---|
| 329 | if espacio==TAB:#si es una caracter tab? |
|---|
| 330 | numtabs=numtabs/self.GetTabWidth() |
|---|
| 331 | linea=linea.strip() |
|---|
| 332 | if len(linea)>0: |
|---|
| 333 | ban,m=self.Tabular(linea) |
|---|
| 334 | else: |
|---|
| 335 | ban=False |
|---|
| 336 | m=0 |
|---|
| 337 | if ban: |
|---|
| 338 | if espacio==TAB: |
|---|
| 339 | numtabs=numtabs+1 |
|---|
| 340 | else: |
|---|
| 341 | numtabs=numtabs+self.GetTabWidth() |
|---|
| 342 | self.NuevaLinea() |
|---|
| 343 | self.AddText( ((espacio*numtabs)+(ESP*m)) ) |
|---|
| 344 | else : |
|---|
| 345 | self.NuevaLinea() |
|---|
| 346 | self.AddText( ((espacio*pos)+(ESP*m)) ) |
|---|
| 347 | |
|---|
| 348 | def OnKeyPressed(self, event): |
|---|
| 349 | """ |
|---|
| 350 | Funcion sobrecargada que se ejecuta cuando alguna tecla es presionada |
|---|
| 351 | """ |
|---|
| 352 | if self.CallTipActive(): |
|---|
| 353 | self.CallTipCancel() |
|---|
| 354 | |
|---|
| 355 | #Para que funcione en el wx2.7 |
|---|
| 356 | if callable(event.KeyCode): |
|---|
| 357 | key = event.KeyCode() |
|---|
| 358 | else: |
|---|
| 359 | key = event.KeyCode |
|---|
| 360 | band=True |
|---|
| 361 | |
|---|
| 362 | if key == wx.WXK_RETURN and not self.AutoCompActive(): |
|---|
| 363 | #Estamos creando una nueva línea agreguémosla al ClassBrowser |
|---|
| 364 | #self.AgregarLineaAlClassBrowser() |
|---|
| 365 | band=False |
|---|
| 366 | self.CalcularTab() |
|---|
| 367 | if key == wx.WXK_SPACE and event.ControlDown(): |
|---|
| 368 | pos = self.GetCurrentPos() |
|---|
| 369 | # Tips |
|---|
| 370 | if event.ShiftDown(): |
|---|
| 371 | self.CallTipSetBackground("white") |
|---|
| 372 | self.CallTipShow(pos, 'El editor del Pyragua\n\n' |
|---|
| 373 | 'Desarrollado por Pyrhox\n' |
|---|
| 374 | 'UTP.') |
|---|
| 375 | # Code completion |
|---|
| 376 | else: |
|---|
| 377 | self.AutoCompletar(event) |
|---|
| 378 | else: |
|---|
| 379 | if band: |
|---|
| 380 | event.Skip() |
|---|
| 381 | |
|---|
| 382 | def CompletarDosPuntos(self): |
|---|
| 383 | """Metodo que pone el caracter ':' como ayuda |
|---|
| 384 | """ |
|---|
| 385 | palabras=['if', 'while', 'for', 'class', 'try','else','except','elif']#las palabras reservadas usadas al antes de ':' |
|---|
| 386 | (linea,pos)=self.GetCurLine()#devuelve la linea actual y la posicion del cursor en esta linea |
|---|
| 387 | #Quito la basura |
|---|
| 388 | linea=linea.strip() |
|---|
| 389 | if len(linea)==0: |
|---|
| 390 | return |
|---|
| 391 | token=self.GetTokens(linea)[-1]#Se toma el ultima palabra de la linea |
|---|
| 392 | #Vuelvo a quitar la basura |
|---|
| 393 | token=token.strip() |
|---|
| 394 | if token=='def': |
|---|
| 395 | numtabs=self.GetLineIndentation(self.GetCurrentLine()) |
|---|
| 396 | if numtabs>=1:#Esto es un machetazo para saber si la declaracion esta dentro de una clase |
|---|
| 397 | #si tiene tabulaciones la funcion deberia estar en una clase por eso el self (Ley de Charles) |
|---|
| 398 | pos=self.GetCurrentPos() |
|---|
| 399 | self.AddText(' ( self ):') |
|---|
| 400 | self.SetCurrentPos(pos) |
|---|
| 401 | else: |
|---|
| 402 | pos=self.GetCurrentPos() |
|---|
| 403 | self.AddText(' ( ):') |
|---|
| 404 | self.SetCurrentPos(pos) |
|---|
| 405 | if token in palabras: |
|---|
| 406 | pos=self.GetCurrentPos() |
|---|
| 407 | self.AddText(' :') |
|---|
| 408 | self.SetCurrentPos(pos) |
|---|
| 409 | def TomarTamIdentacion( self, numLinea ): |
|---|
| 410 | linea=self.GetLine(numLinea) |
|---|
| 411 | aux_linea=linea.lstrip() |
|---|
| 412 | tam=len(linea) - len(aux_linea) |
|---|
| 413 | return tam |
|---|
| 414 | |
|---|
| 415 | def STCComentarBloque( self ): |
|---|
| 416 | """ |
|---|
| 417 | Metodo que comenta agregando el caracter '#_' al inicio de cada linea seleccionada |
|---|
| 418 | """ |
|---|
| 419 | cad=self.CadenaParaComentar |
|---|
| 420 | a,b=self.GetSelection()#se toma la posicion inicial y final de la seleccion |
|---|
| 421 | #a=posicion inicial de la seleccion |
|---|
| 422 | #b=posicion final de la seleccion |
|---|
| 423 | A=self.LineFromPosition(a)#se toma el numero de la linea donde inicia la seleccion |
|---|
| 424 | B=self.LineFromPosition(b)#se toma el numero de la linea donde finaliza la seleccion |
|---|
| 425 | for x in range(A,B+1) :#recorre las lineas seleccionadas |
|---|
| 426 | pos=self.PositionFromLine(x)#toma la posicion al inicio de cada linea |
|---|
| 427 | numtabs=self.TomarTamIdentacion(x) |
|---|
| 428 | #numtabs=self.GetLineIndentation(x) |
|---|
| 429 | tamLinea=len( self.GetLine(x).strip() ) |
|---|
| 430 | if tamLinea > 0: |
|---|
| 431 | if len(cad) > tamLinea: |
|---|
| 432 | self.InsertText(pos+numtabs,cad) |
|---|
| 433 | else: |
|---|
| 434 | text=self.GetTextRange( pos+numtabs , pos+numtabs+len(cad) ) |
|---|
| 435 | if len(text)>0 and (not text==cad) : |
|---|
| 436 | self.InsertText(pos+numtabs,cad) |
|---|
| 437 | |
|---|
| 438 | def STCDesComentarBloque( self ): |
|---|
| 439 | """ |
|---|
| 440 | Metodo que descomenta removiendo el caracter '#_' al inicio de cada linea seleccionada |
|---|
| 441 | """ |
|---|
| 442 | cad=self.CadenaParaComentar |
|---|
| 443 | a,b=self.GetSelection()#se toma la posicion inicial y final de la seleccion |
|---|
| 444 | #a=posicion inicial de la seleccion |
|---|
| 445 | #b=posicion final de la seleccion |
|---|
| 446 | A=self.LineFromPosition(a)#se toma el numero de la linea donde inicia la seleccion |
|---|
| 447 | B=self.LineFromPosition(b)#se toma el numero de la linea donde finaliza la seleccion |
|---|
| 448 | targetstart=self.GetTargetStart() |
|---|
| 449 | targetend=self.GetTargetEnd() |
|---|
| 450 | for x in range(A,B+1) :#recorre las lineas seleccionadas |
|---|
| 451 | pos=self.PositionFromLine(x)#toma la posicion al inicio de cada linea |
|---|
| 452 | numtabs=self.TomarTamIdentacion(x) |
|---|
| 453 | #numtabs=self.GetLineIndentation(x) |
|---|
| 454 | tamLinea=len(self.GetLine(x).strip()) |
|---|
| 455 | if tamLinea>=len(cad) : |
|---|
| 456 | text=self.GetTextRange(pos+numtabs,pos+numtabs+len(cad)) |
|---|
| 457 | if text==cad :#esta comentada al inicio? |
|---|
| 458 | self.SetTargetStart(pos+numtabs) |
|---|
| 459 | self.SetTargetEnd(pos+numtabs+len(cad)) |
|---|
| 460 | self.ReplaceTarget('')#reemplaza el caracter '#_' por el caracter '' |
|---|
| 461 | self.SetTargetStart(targetstart) |
|---|
| 462 | self.SetTargetEnd(targetend) |
|---|
| 463 | |
|---|
| 464 | def ChangeText ( self , evento): |
|---|
| 465 | #self.AcusarModificacion() |
|---|
| 466 | evento.Skip() |
|---|
| 467 | |
|---|
| 468 | def OnChar(self,event): |
|---|
| 469 | """Evento disparado cuando se recibe un caracter""" |
|---|
| 470 | #Para que funcione en el wx2.7 |
|---|
| 471 | if callable(event.KeyCode): |
|---|
| 472 | key = event.KeyCode() |
|---|
| 473 | else: |
|---|
| 474 | key = event.KeyCode |
|---|
| 475 | pos=self.GetCurrentPos() |
|---|
| 476 | self.AcusarModificacion() |
|---|
| 477 | if key==ord(' '):#pregunta si escribio un espacio |
|---|
| 478 | self.CompletarDosPuntos() |
|---|
| 479 | self.AddText(' ') |
|---|
| 480 | elif key==ord("."): |
|---|
| 481 | if not self.AutoCompletar(event): |
|---|
| 482 | #Un error en el autocompletar |
|---|
| 483 | #Para que se agregue el caracter presionado |
|---|
| 484 | event.Skip() |
|---|
| 485 | else: |
|---|
| 486 | #Procesar normalmente |
|---|
| 487 | event.Skip() |
|---|
| 488 | |
|---|
| 489 | def OnUpdateUI(self, evt): |
|---|
| 490 | """ |
|---|
| 491 | Reviza si hay parentesis, llaves o corchetes para resaltar. |
|---|
| 492 | """ |
|---|
| 493 | # check for matching braces |
|---|
| 494 | braceAtCaret = -1 |
|---|
| 495 | braceOpposite = -1 |
|---|
| 496 | charBefore = None |
|---|
| 497 | caretPos = self.GetCurrentPos() |
|---|
| 498 | |
|---|
| 499 | if caretPos > 0: |
|---|
| 500 | charBefore = self.GetCharAt(caretPos - 1) |
|---|
| 501 | styleBefore = self.GetStyleAt(caretPos - 1) |
|---|
| 502 | |
|---|
| 503 | # check before |
|---|
| 504 | if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR: |
|---|
| 505 | braceAtCaret = caretPos - 1 |
|---|
| 506 | |
|---|
| 507 | # check after |
|---|
| 508 | if braceAtCaret < 0: |
|---|
| 509 | charAfter = self.GetCharAt(caretPos) |
|---|
| 510 | styleAfter = self.GetStyleAt(caretPos) |
|---|
| 511 | |
|---|
| 512 | if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR: |
|---|
| 513 | braceAtCaret = caretPos |
|---|
| 514 | |
|---|
| 515 | if braceAtCaret >= 0: |
|---|
| 516 | braceOpposite = self.BraceMatch(braceAtCaret) |
|---|
| 517 | |
|---|
| 518 | if braceAtCaret != -1 and braceOpposite == -1: |
|---|
| 519 | self.BraceBadLight(braceAtCaret) |
|---|
| 520 | else: |
|---|
| 521 | self.BraceHighlight(braceAtCaret, braceOpposite) |
|---|
| 522 | #pt = self.PointFromPosition(braceOpposite) |
|---|
| 523 | #self.Refresh(True, wxRect(pt.x, pt.y, 5,5)) |
|---|
| 524 | #print pt |
|---|
| 525 | #self.Refresh(False) |
|---|
| 526 | |
|---|
| 527 | |
|---|
| 528 | def OnMarginClick(self, evt): |
|---|
| 529 | """ |
|---|
| 530 | Muestra o esconde el texto de los bloques. |
|---|
| 531 | """ |
|---|
| 532 | # fold and unfold as needed |
|---|
| 533 | if evt.GetMargin() == 2: |
|---|
| 534 | if evt.GetShift() and evt.GetControl(): |
|---|
| 535 | self.FoldAll() |
|---|
| 536 | else: |
|---|
| 537 | lineClicked = self.LineFromPosition(evt.GetPosition()) |
|---|
| 538 | |
|---|
| 539 | if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG: |
|---|
| 540 | if evt.GetShift(): |
|---|
| 541 | self.SetFoldExpanded(lineClicked, True) |
|---|
| 542 | self.Expand(lineClicked, True, True, 1) |
|---|
| 543 | elif evt.GetControl(): |
|---|
| 544 | if self.GetFoldExpanded(lineClicked): |
|---|
| 545 | self.SetFoldExpanded(lineClicked, False) |
|---|
| 546 | self.Expand(lineClicked, False, True, 0) |
|---|
| 547 | else: |
|---|
| 548 | self.SetFoldExpanded(lineClicked, True) |
|---|
| 549 | self.Expand(lineClicked, True, True, 100) |
|---|
| 550 | else: |
|---|
| 551 | self.ToggleFold(lineClicked) |
|---|
| 552 | |
|---|
| 553 | |
|---|
| 554 | def FoldAll(self): |
|---|
| 555 | """ |
|---|
| 556 | Esconde todos bloques. |
|---|
| 557 | """ |
|---|
| 558 | lineCount = self.GetLineCount() |
|---|
| 559 | expanding = True |
|---|
| 560 | |
|---|
| 561 | # find out if we are folding or unfolding |
|---|
| 562 | for lineNum in range(lineCount): |
|---|
| 563 | if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG: |
|---|
| 564 | expanding = not self.GetFoldExpanded(lineNum) |
|---|
| 565 | break |
|---|
| 566 | |
|---|
| 567 | lineNum = 0 |
|---|
| 568 | |
|---|
| 569 | while lineNum < lineCount: |
|---|
| 570 | level = self.GetFoldLevel(lineNum) |
|---|
| 571 | if level & stc.STC_FOLDLEVELHEADERFLAG and \ |
|---|
| 572 | (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE: |
|---|
| 573 | |
|---|
| 574 | if expanding: |
|---|
| 575 | self.SetFoldExpanded(lineNum, True) |
|---|
| 576 | lineNum = self.Expand(lineNum, True) |
|---|
| 577 | lineNum = lineNum - 1 |
|---|
| 578 | else: |
|---|
| 579 | lastChild = self.GetLastChild(lineNum, -1) |
|---|
| 580 | self.SetFoldExpanded(lineNum, False) |
|---|
| 581 | |
|---|
| 582 | if lastChild > lineNum: |
|---|
| 583 | self.HideLines(lineNum+1, lastChild) |
|---|
| 584 | |
|---|
| 585 | lineNum = lineNum + 1 |
|---|
| 586 | |
|---|
| 587 | |
|---|
| 588 | |
|---|
| 589 | def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): |
|---|
| 590 | """ |
|---|
| 591 | Muestra el texto de un bloque. |
|---|
| 592 | """ |
|---|
| 593 | lastChild = self.GetLastChild(line, level) |
|---|
| 594 | line = line + 1 |
|---|
| 595 | |
|---|
| 596 | while line <= lastChild: |
|---|
| 597 | if force: |
|---|
| 598 | if visLevels > 0: |
|---|
| 599 | self.ShowLines(line, line) |
|---|
| 600 | else: |
|---|
| 601 | self.HideLines(line, line) |
|---|
| 602 | else: |
|---|
| 603 | if doExpand: |
|---|
| 604 | self.ShowLines(line, line) |
|---|
| 605 | |
|---|
| 606 | if level == -1: |
|---|
| 607 | level = self.GetFoldLevel(line) |
|---|
| 608 | |
|---|
| 609 | if level & stc.STC_FOLDLEVELHEADERFLAG: |
|---|
| 610 | if force: |
|---|
| 611 | if visLevels > 1: |
|---|
| 612 | self.SetFoldExpanded(line, True) |
|---|
| 613 | else: |
|---|
| 614 | self.SetFoldExpanded(line, False) |
|---|
| 615 | |
|---|
| 616 | line = self.Expand(line, doExpand, force, visLevels-1) |
|---|
| 617 | |
|---|
| 618 | else: |
|---|
| 619 | if doExpand and self.GetFoldExpanded(line): |
|---|
| 620 | line = self.Expand(line, True, force, visLevels-1) |
|---|
| 621 | else: |
|---|
| 622 | line = self.Expand(line, False, force, visLevels-1) |
|---|
| 623 | else: |
|---|
| 624 | line = line + 1 |
|---|
| 625 | |
|---|
| 626 | return line |
|---|
| 627 | |
|---|
| 628 | |
|---|
| 629 | |
|---|
| 630 | def AcusarModificacion(self): |
|---|
| 631 | """Esta función se encarga de dibujar un * en el nombre de la |
|---|
| 632 | pestaña del panel para avisar que se ha modificado el archivo""" |
|---|
| 633 | #La instancia de archivo que me contiene |
|---|
| 634 | archivo=self.archivo |
|---|
| 635 | #Este es el notebook donde estoy metido |
|---|
| 636 | note_book=archivo.padre |
|---|
| 637 | #Cuando es un archivo nuevo sin guardar no pongo nada |
|---|
| 638 | if archivo.nombre!="": |
|---|
| 639 | note_book.SetPageText(note_book.GetSelection(), |
|---|
| 640 | str(note_book.GetSelection()+1)+' '+os.path.basename(archivo.nombre)+' '+"*") |
|---|
| 641 | #Código para agregar un * cuando se modifica el archivo |
|---|
| 642 | |
|---|
| 643 | def DesAcusarModificacion(self): |
|---|
| 644 | """Esta función se encarga de quitar el * del nombre de la |
|---|
| 645 | pestaña del panel para avisar que no se ha modificado el archivo""" |
|---|
| 646 | #La instancia de archivo que me contiene |
|---|
| 647 | archivo=self.archivo |
|---|
| 648 | #Este es el notebook donde estoy metido |
|---|
| 649 | note_book=archivo.padre |
|---|
| 650 | #Cuando es un archivo nuevo sin guardar no pongo nada |
|---|
| 651 | if archivo.nombre!="": |
|---|
| 652 | note_book.SetPageText(note_book.GetSelection(), |
|---|
| 653 | str(note_book.GetSelection()+1)+' '+os.path.basename(archivo.nombre)) |
|---|
| 654 | #Código para quitar el * cuando no se modifica el archivo |
|---|
| 655 | |
|---|
| 656 | def GetTokens(self,linea): |
|---|
| 657 | """Toma una cadena de código python y la parte por tokens""" |
|---|
| 658 | seps=[" ",",","=","(","|"] |
|---|
| 659 | pal="" |
|---|
| 660 | tokens=[] |
|---|
| 661 | for c in linea: |
|---|
| 662 | if c in seps: |
|---|
| 663 | tokens.append(pal) |
|---|
| 664 | pal="" |
|---|
| 665 | else: |
|---|
| 666 | pal+=c |
|---|
| 667 | if pal!="": |
|---|
| 668 | tokens.append(pal) |
|---|
| 669 | return tokens |
|---|
| 670 | |
|---|
| 671 | def AgregarLineaAlClassBrowser ( self ): |
|---|
| 672 | nombreArchivo=self.archivo.nombre |
|---|
| 673 | linea=self.GetCurLine()[0] |
|---|
| 674 | lineaNum=self.GetCurrentLine() |
|---|
| 675 | self.pyragua.cb.AddLine( nombreArchivo, linea , lineaNum) |
|---|
| 676 | self.pyragua.finicial.pCodigo.aCodigo.Actualizar() |
|---|
| 677 | |
|---|
| 678 | ########AUTOCOMPLETAR |
|---|
| 679 | def ACLimpiarGetToken( self , linea): |
|---|
| 680 | """Mira la línea actual, la parte por tokens y retorna solo |
|---|
| 681 | el último token""" |
|---|
| 682 | #Quito la basura |
|---|
| 683 | linea.strip() |
|---|
| 684 | linea=EliminarEOLS(linea) |
|---|
| 685 | if len(linea)==0 : |
|---|
| 686 | return False |
|---|
| 687 | |
|---|
| 688 | #Parto la línea por tokens, y tomo solo el último elemento |
|---|
| 689 | token=self.GetTokens(linea)[-1] |
|---|
| 690 | #Vuelvo a quitar la basura |
|---|
| 691 | token.strip() |
|---|
| 692 | return token |
|---|
| 693 | |
|---|
| 694 | def ACQuitarPunto( self, cadena ): |
|---|
| 695 | "Elimina el el punto final de una cadena" |
|---|
| 696 | if cadena[-1:]=='.': |
|---|
| 697 | #Elimino el punto |
|---|
| 698 | cadena=cadena[:-1] |
|---|
| 699 | return cadena |
|---|
| 700 | |
|---|
| 701 | def AutoCompletar(self,evento): |
|---|
| 702 | |
|---|
| 703 | """Este método se encargará de autocompletar según lo que esté |
|---|
| 704 | escrito, retorna True si fue capaz de realizarlo""" |
|---|
| 705 | self.AutoCompSetIgnoreCase(False) |
|---|
| 706 | (linea,pos)=self.GetCurLine() |
|---|
| 707 | token=self.ACLimpiarGetToken(linea) |
|---|
| 708 | token=self.ACQuitarPunto(token) |
|---|
| 709 | |
|---|
| 710 | objeto=self.ACEvaluar(token,linea) |
|---|
| 711 | |
|---|
| 712 | if not objeto: |
|---|
| 713 | return False |
|---|
| 714 | listaOps=dir(objeto) |
|---|
| 715 | #Le adiciono a cada elemento el token |
|---|
| 716 | #listaOps=[token+"."+x for x in listaOps] |
|---|
| 717 | #self.autoComplete(object=1) |
|---|
| 718 | if listaOps!=[]: |
|---|
| 719 | self.AddText(".") |
|---|
| 720 | self.AutoCompShow(0," ".join(listaOps)) |
|---|
| 721 | return True |
|---|
| 722 | else: |
|---|
| 723 | return False |
|---|
| 724 | |
|---|
| 725 | def ACEvaluar(self,word, linea): |
|---|
| 726 | """Código tomado del spe, retorna un objeto obtenido con eval, |
|---|
| 727 | modificado""" |
|---|
| 728 | #Ya existe la palabra en el namespace, retornar lo que tenemos |
|---|
| 729 | if word in self.namespace.keys(): |
|---|
| 730 | return self.namespace[word] |
|---|
| 731 | #Si aún no está evaluar |
|---|
| 732 | try: |
|---|
| 733 | objeto=eval(word,self.namespace) |
|---|
| 734 | except: |
|---|
| 735 | try: |
|---|
| 736 | #Aún no ha sido importado, lo importo |
|---|
| 737 | objeto=__import__(word) |
|---|
| 738 | except ImportError: |
|---|
| 739 | #El módulo aún no existe, busco a ver si es uno de los que |
|---|
| 740 | #se están escribiendo en este momento |
|---|
| 741 | objeto=None |
|---|
| 742 | except : |
|---|
| 743 | print "Error en autocompletar", sys.exc_info |
|---|
| 744 | return None |
|---|
| 745 | |
|---|
| 746 | #Agrego el objeto a mi lista |
|---|
| 747 | self.namespace[word]=objeto |
|---|
| 748 | return self.namespace[word] |
|---|
| 749 | |
|---|
| 750 | |
|---|
| 751 | ########FIN AUTOCOMPLETAR |
|---|