Spaces:
Sleeping
Sleeping
| # An Python interface to the Scintilla control. | |
| # | |
| # Exposes Python classes that allow you to use Scintilla as | |
| # a "standard" MFC edit control (eg, control.GetTextLength(), control.GetSel() | |
| # plus many Scintilla specific features (eg control.SCIAddStyledText()) | |
| import array | |
| import os | |
| import struct | |
| import win32api | |
| import win32con | |
| import win32ui | |
| from pywin import default_scintilla_encoding | |
| from pywin.mfc import window | |
| from . import scintillacon | |
| # Load Scintilla.dll to get access to the control. | |
| # We expect to find this in the same directory as win32ui.pyd | |
| dllid = None | |
| if win32ui.debug: # If running _d version of Pythonwin... | |
| try: | |
| dllid = win32api.LoadLibrary( | |
| os.path.join(os.path.split(win32ui.__file__)[0], "Scintilla_d.DLL") | |
| ) | |
| except ( | |
| win32api.error | |
| ): # Not there - we dont _need_ a debug ver, so ignore this error. | |
| pass | |
| if dllid is None: | |
| try: | |
| dllid = win32api.LoadLibrary( | |
| os.path.join(os.path.split(win32ui.__file__)[0], "Scintilla.DLL") | |
| ) | |
| except win32api.error: | |
| pass | |
| if dllid is None: | |
| # Still not there - lets see if Windows can find it by searching? | |
| dllid = win32api.LoadLibrary("Scintilla.DLL") | |
| # null_byte is str in py2k, bytes on py3k | |
| null_byte = "\0".encode("ascii") | |
| ## These are from Richedit.h - need to add to win32con or commctrl | |
| EM_GETTEXTRANGE = 1099 | |
| EM_EXLINEFROMCHAR = 1078 | |
| EM_FINDTEXTEX = 1103 | |
| EM_GETSELTEXT = 1086 | |
| EM_EXSETSEL = win32con.WM_USER + 55 | |
| class ScintillaNotification: | |
| def __init__(self, **args): | |
| self.__dict__.update(args) | |
| class ScintillaControlInterface: | |
| def SCIUnpackNotifyMessage(self, msg): | |
| format = "iiiiPiiiPPiiii" | |
| bytes = win32ui.GetBytes(msg, struct.calcsize(format)) | |
| ( | |
| position, | |
| ch, | |
| modifiers, | |
| modificationType, | |
| text_ptr, | |
| length, | |
| linesAdded, | |
| msg, | |
| wParam, | |
| lParam, | |
| line, | |
| foldLevelNow, | |
| foldLevelPrev, | |
| margin, | |
| ) = struct.unpack(format, bytes) | |
| return ScintillaNotification( | |
| position=position, | |
| ch=ch, | |
| modifiers=modifiers, | |
| modificationType=modificationType, | |
| text_ptr=text_ptr, | |
| length=length, | |
| linesAdded=linesAdded, | |
| msg=msg, | |
| wParam=wParam, | |
| lParam=lParam, | |
| line=line, | |
| foldLevelNow=foldLevelNow, | |
| foldLevelPrev=foldLevelPrev, | |
| margin=margin, | |
| ) | |
| def SCIAddText(self, text): | |
| self.SendMessage( | |
| scintillacon.SCI_ADDTEXT, text.encode(default_scintilla_encoding) | |
| ) | |
| def SCIAddStyledText(self, text, style=None): | |
| # If style is None, text is assumed to be a "native" Scintilla buffer. | |
| # If style is specified, text is a normal string, and the style is | |
| # assumed to apply to the entire string. | |
| if style is not None: | |
| text = list(map(lambda char, style=style: char + chr(style), text)) | |
| text = "".join(text) | |
| self.SendMessage( | |
| scintillacon.SCI_ADDSTYLEDTEXT, text.encode(default_scintilla_encoding) | |
| ) | |
| def SCIInsertText(self, text, pos=-1): | |
| # SCIInsertText allows unicode or bytes - but if they are bytes, | |
| # the caller must ensure it is encoded correctly. | |
| if isinstance(text, str): | |
| text = text.encode(default_scintilla_encoding) | |
| self.SendScintilla(scintillacon.SCI_INSERTTEXT, pos, text + null_byte) | |
| def SCISetSavePoint(self): | |
| self.SendScintilla(scintillacon.SCI_SETSAVEPOINT) | |
| def SCISetUndoCollection(self, collectFlag): | |
| self.SendScintilla(scintillacon.SCI_SETUNDOCOLLECTION, collectFlag) | |
| def SCIBeginUndoAction(self): | |
| self.SendScintilla(scintillacon.SCI_BEGINUNDOACTION) | |
| def SCIEndUndoAction(self): | |
| self.SendScintilla(scintillacon.SCI_ENDUNDOACTION) | |
| def SCIGetCurrentPos(self): | |
| return self.SendScintilla(scintillacon.SCI_GETCURRENTPOS) | |
| def SCIGetCharAt(self, pos): | |
| # Must ensure char is unsigned! | |
| return chr(self.SendScintilla(scintillacon.SCI_GETCHARAT, pos) & 0xFF) | |
| def SCIGotoLine(self, line): | |
| self.SendScintilla(scintillacon.SCI_GOTOLINE, line) | |
| def SCIBraceMatch(self, pos, maxReStyle): | |
| return self.SendScintilla(scintillacon.SCI_BRACEMATCH, pos, maxReStyle) | |
| def SCIBraceHighlight(self, pos, posOpposite): | |
| return self.SendScintilla(scintillacon.SCI_BRACEHIGHLIGHT, pos, posOpposite) | |
| def SCIBraceBadHighlight(self, pos): | |
| return self.SendScintilla(scintillacon.SCI_BRACEBADLIGHT, pos) | |
| #################################### | |
| # Styling | |
| # def SCIColourise(self, start=0, end=-1): | |
| # NOTE - dependent on of we use builtin lexer, so handled below. | |
| def SCIGetEndStyled(self): | |
| return self.SendScintilla(scintillacon.SCI_GETENDSTYLED) | |
| def SCIStyleSetFore(self, num, v): | |
| return self.SendScintilla(scintillacon.SCI_STYLESETFORE, num, v) | |
| def SCIStyleSetBack(self, num, v): | |
| return self.SendScintilla(scintillacon.SCI_STYLESETBACK, num, v) | |
| def SCIStyleSetEOLFilled(self, num, v): | |
| return self.SendScintilla(scintillacon.SCI_STYLESETEOLFILLED, num, v) | |
| def SCIStyleSetFont(self, num, name, characterset=0): | |
| buff = (name + "\0").encode(default_scintilla_encoding) | |
| self.SendScintilla(scintillacon.SCI_STYLESETFONT, num, buff) | |
| self.SendScintilla(scintillacon.SCI_STYLESETCHARACTERSET, num, characterset) | |
| def SCIStyleSetBold(self, num, bBold): | |
| self.SendScintilla(scintillacon.SCI_STYLESETBOLD, num, bBold) | |
| def SCIStyleSetItalic(self, num, bItalic): | |
| self.SendScintilla(scintillacon.SCI_STYLESETITALIC, num, bItalic) | |
| def SCIStyleSetSize(self, num, size): | |
| self.SendScintilla(scintillacon.SCI_STYLESETSIZE, num, size) | |
| def SCIGetViewWS(self): | |
| return self.SendScintilla(scintillacon.SCI_GETVIEWWS) | |
| def SCISetViewWS(self, val): | |
| self.SendScintilla(scintillacon.SCI_SETVIEWWS, not (val == 0)) | |
| self.InvalidateRect() | |
| def SCISetIndentationGuides(self, val): | |
| self.SendScintilla(scintillacon.SCI_SETINDENTATIONGUIDES, val) | |
| def SCIGetIndentationGuides(self): | |
| return self.SendScintilla(scintillacon.SCI_GETINDENTATIONGUIDES) | |
| def SCISetIndent(self, val): | |
| self.SendScintilla(scintillacon.SCI_SETINDENT, val) | |
| def SCIGetIndent(self, val): | |
| return self.SendScintilla(scintillacon.SCI_GETINDENT) | |
| def SCIGetViewEOL(self): | |
| return self.SendScintilla(scintillacon.SCI_GETVIEWEOL) | |
| def SCISetViewEOL(self, val): | |
| self.SendScintilla(scintillacon.SCI_SETVIEWEOL, not (val == 0)) | |
| self.InvalidateRect() | |
| def SCISetTabWidth(self, width): | |
| self.SendScintilla(scintillacon.SCI_SETTABWIDTH, width, 0) | |
| def SCIStartStyling(self, pos, mask): | |
| self.SendScintilla(scintillacon.SCI_STARTSTYLING, pos, mask) | |
| def SCISetStyling(self, pos, attr): | |
| self.SendScintilla(scintillacon.SCI_SETSTYLING, pos, attr) | |
| def SCISetStylingEx(self, ray): # ray is an array. | |
| address, length = ray.buffer_info() | |
| self.SendScintilla(scintillacon.SCI_SETSTYLINGEX, length, address) | |
| def SCIGetStyleAt(self, pos): | |
| return self.SendScintilla(scintillacon.SCI_GETSTYLEAT, pos) | |
| def SCISetMarginWidth(self, width): | |
| self.SendScintilla(scintillacon.SCI_SETMARGINWIDTHN, 1, width) | |
| def SCISetMarginWidthN(self, n, width): | |
| self.SendScintilla(scintillacon.SCI_SETMARGINWIDTHN, n, width) | |
| def SCISetFoldFlags(self, flags): | |
| self.SendScintilla(scintillacon.SCI_SETFOLDFLAGS, flags) | |
| # Markers | |
| def SCIMarkerDefineAll(self, markerNum, markerType, fore, back): | |
| self.SCIMarkerDefine(markerNum, markerType) | |
| self.SCIMarkerSetFore(markerNum, fore) | |
| self.SCIMarkerSetBack(markerNum, back) | |
| def SCIMarkerDefine(self, markerNum, markerType): | |
| self.SendScintilla(scintillacon.SCI_MARKERDEFINE, markerNum, markerType) | |
| def SCIMarkerSetFore(self, markerNum, fore): | |
| self.SendScintilla(scintillacon.SCI_MARKERSETFORE, markerNum, fore) | |
| def SCIMarkerSetBack(self, markerNum, back): | |
| self.SendScintilla(scintillacon.SCI_MARKERSETBACK, markerNum, back) | |
| def SCIMarkerAdd(self, lineNo, markerNum): | |
| self.SendScintilla(scintillacon.SCI_MARKERADD, lineNo, markerNum) | |
| def SCIMarkerDelete(self, lineNo, markerNum): | |
| self.SendScintilla(scintillacon.SCI_MARKERDELETE, lineNo, markerNum) | |
| def SCIMarkerDeleteAll(self, markerNum=-1): | |
| self.SendScintilla(scintillacon.SCI_MARKERDELETEALL, markerNum) | |
| def SCIMarkerGet(self, lineNo): | |
| return self.SendScintilla(scintillacon.SCI_MARKERGET, lineNo) | |
| def SCIMarkerNext(self, lineNo, markerNum): | |
| return self.SendScintilla(scintillacon.SCI_MARKERNEXT, lineNo, markerNum) | |
| def SCICancel(self): | |
| self.SendScintilla(scintillacon.SCI_CANCEL) | |
| # AutoComplete | |
| def SCIAutoCShow(self, text): | |
| if type(text) in [type([]), type(())]: | |
| text = " ".join(text) | |
| buff = (text + "\0").encode(default_scintilla_encoding) | |
| return self.SendScintilla(scintillacon.SCI_AUTOCSHOW, 0, buff) | |
| def SCIAutoCCancel(self): | |
| self.SendScintilla(scintillacon.SCI_AUTOCCANCEL) | |
| def SCIAutoCActive(self): | |
| return self.SendScintilla(scintillacon.SCI_AUTOCACTIVE) | |
| def SCIAutoCComplete(self): | |
| return self.SendScintilla(scintillacon.SCI_AUTOCCOMPLETE) | |
| def SCIAutoCStops(self, stops): | |
| buff = (stops + "\0").encode(default_scintilla_encoding) | |
| self.SendScintilla(scintillacon.SCI_AUTOCSTOPS, 0, buff) | |
| def SCIAutoCSetAutoHide(self, hide): | |
| self.SendScintilla(scintillacon.SCI_AUTOCSETAUTOHIDE, hide) | |
| def SCIAutoCSetFillups(self, fillups): | |
| self.SendScintilla(scintillacon.SCI_AUTOCSETFILLUPS, fillups) | |
| # Call tips | |
| def SCICallTipShow(self, text, pos=-1): | |
| if pos == -1: | |
| pos = self.GetSel()[0] | |
| buff = (text + "\0").encode(default_scintilla_encoding) | |
| self.SendScintilla(scintillacon.SCI_CALLTIPSHOW, pos, buff) | |
| def SCICallTipCancel(self): | |
| self.SendScintilla(scintillacon.SCI_CALLTIPCANCEL) | |
| def SCICallTipActive(self): | |
| return self.SendScintilla(scintillacon.SCI_CALLTIPACTIVE) | |
| def SCICallTipPosStart(self): | |
| return self.SendScintilla(scintillacon.SCI_CALLTIPPOSSTART) | |
| def SCINewline(self): | |
| self.SendScintilla(scintillacon.SCI_NEWLINE) | |
| # Lexer etc | |
| def SCISetKeywords(self, keywords, kw_list_no=0): | |
| buff = (keywords + "\0").encode(default_scintilla_encoding) | |
| self.SendScintilla(scintillacon.SCI_SETKEYWORDS, kw_list_no, buff) | |
| def SCISetProperty(self, name, value): | |
| name_buff = array.array("b", (name + "\0").encode(default_scintilla_encoding)) | |
| val_buff = array.array( | |
| "b", (str(value) + "\0").encode(default_scintilla_encoding) | |
| ) | |
| address_name_buffer = name_buff.buffer_info()[0] | |
| address_val_buffer = val_buff.buffer_info()[0] | |
| self.SendScintilla( | |
| scintillacon.SCI_SETPROPERTY, address_name_buffer, address_val_buffer | |
| ) | |
| def SCISetStyleBits(self, nbits): | |
| self.SendScintilla(scintillacon.SCI_SETSTYLEBITS, nbits) | |
| # Folding | |
| def SCIGetFoldLevel(self, lineno): | |
| return self.SendScintilla(scintillacon.SCI_GETFOLDLEVEL, lineno) | |
| def SCIToggleFold(self, lineno): | |
| return self.SendScintilla(scintillacon.SCI_TOGGLEFOLD, lineno) | |
| def SCIEnsureVisible(self, lineno): | |
| self.SendScintilla(scintillacon.SCI_ENSUREVISIBLE, lineno) | |
| def SCIGetFoldExpanded(self, lineno): | |
| return self.SendScintilla(scintillacon.SCI_GETFOLDEXPANDED, lineno) | |
| # right edge | |
| def SCISetEdgeColumn(self, edge): | |
| self.SendScintilla(scintillacon.SCI_SETEDGECOLUMN, edge) | |
| def SCIGetEdgeColumn(self): | |
| return self.SendScintilla(scintillacon.SCI_GETEDGECOLUMN) | |
| def SCISetEdgeMode(self, mode): | |
| self.SendScintilla(scintillacon.SCI_SETEDGEMODE, mode) | |
| def SCIGetEdgeMode(self): | |
| return self.SendScintilla(scintillacon.SCI_GETEDGEMODE) | |
| def SCISetEdgeColor(self, color): | |
| self.SendScintilla(scintillacon.SCI_SETEDGECOLOUR, color) | |
| def SCIGetEdgeColor(self): | |
| return self.SendScintilla(scintillacon.SCI_GETEDGECOLOR) | |
| # Multi-doc | |
| def SCIGetDocPointer(self): | |
| return self.SendScintilla(scintillacon.SCI_GETDOCPOINTER) | |
| def SCISetDocPointer(self, p): | |
| return self.SendScintilla(scintillacon.SCI_SETDOCPOINTER, 0, p) | |
| def SCISetWrapMode(self, mode): | |
| return self.SendScintilla(scintillacon.SCI_SETWRAPMODE, mode) | |
| def SCIGetWrapMode(self): | |
| return self.SendScintilla(scintillacon.SCI_GETWRAPMODE) | |
| class CScintillaEditInterface(ScintillaControlInterface): | |
| def close(self): | |
| self.colorizer = None | |
| def Clear(self): | |
| self.SendScintilla(win32con.WM_CLEAR) | |
| def FindText(self, flags, range, findText): | |
| """LPARAM for EM_FINDTEXTEX: | |
| typedef struct _findtextex { | |
| CHARRANGE chrg; | |
| LPCTSTR lpstrText; | |
| CHARRANGE chrgText;} FINDTEXTEX; | |
| typedef struct _charrange { | |
| LONG cpMin; | |
| LONG cpMax;} CHARRANGE; | |
| """ | |
| findtextex_fmt = "llPll" | |
| ## Scintilla does not handle unicode in EM_FINDTEXT msg (FINDTEXTEX struct) | |
| txt_buff = (findText + "\0").encode(default_scintilla_encoding) | |
| txt_array = array.array("b", txt_buff) | |
| ft_buff = struct.pack( | |
| findtextex_fmt, range[0], range[1], txt_array.buffer_info()[0], 0, 0 | |
| ) | |
| ft_array = array.array("b", ft_buff) | |
| rc = self.SendScintilla(EM_FINDTEXTEX, flags, ft_array.buffer_info()[0]) | |
| ftUnpacked = struct.unpack(findtextex_fmt, ft_array) | |
| return rc, (ftUnpacked[3], ftUnpacked[4]) | |
| def GetSel(self): | |
| currentPos = self.SendScintilla(scintillacon.SCI_GETCURRENTPOS) | |
| anchorPos = self.SendScintilla(scintillacon.SCI_GETANCHOR) | |
| if currentPos < anchorPos: | |
| return (currentPos, anchorPos) | |
| else: | |
| return (anchorPos, currentPos) | |
| return currentPos | |
| def GetSelText(self): | |
| start, end = self.GetSel() | |
| txtBuf = array.array("b", null_byte * (end - start + 1)) | |
| addressTxtBuf = txtBuf.buffer_info()[0] | |
| # EM_GETSELTEXT is documented as returning the number of chars | |
| # not including the NULL, but scintilla includes the NULL. A | |
| # quick glance at the scintilla impl doesn't make this | |
| # obvious - the NULL is included in the 'selection' object | |
| # and reflected in the length of that 'selection' object. | |
| # I expect that is a bug in scintilla and may be fixed by now, | |
| # but we just blindly assume that the last char is \0 and | |
| # strip it. | |
| self.SendScintilla(EM_GETSELTEXT, 0, addressTxtBuf) | |
| return txtBuf.tobytes()[:-1].decode(default_scintilla_encoding) | |
| def SetSel(self, start=0, end=None): | |
| if type(start) == type(()): | |
| assert ( | |
| end is None | |
| ), "If you pass a point in the first param, the second must be None" | |
| start, end = start | |
| elif end is None: | |
| end = start | |
| if start < 0: | |
| start = self.GetTextLength() | |
| if end < 0: | |
| end = self.GetTextLength() | |
| assert start <= self.GetTextLength(), "The start postion is invalid (%d/%d)" % ( | |
| start, | |
| self.GetTextLength(), | |
| ) | |
| assert end <= self.GetTextLength(), "The end postion is invalid (%d/%d)" % ( | |
| end, | |
| self.GetTextLength(), | |
| ) | |
| cr = struct.pack("ll", start, end) | |
| crBuff = array.array("b", cr) | |
| addressCrBuff = crBuff.buffer_info()[0] | |
| rc = self.SendScintilla(EM_EXSETSEL, 0, addressCrBuff) | |
| def GetLineCount(self): | |
| return self.SendScintilla(win32con.EM_GETLINECOUNT) | |
| def LineFromChar(self, charPos=-1): | |
| if charPos == -1: | |
| charPos = self.GetSel()[0] | |
| assert ( | |
| charPos >= 0 and charPos <= self.GetTextLength() | |
| ), "The charPos postion (%s) is invalid (max=%s)" % ( | |
| charPos, | |
| self.GetTextLength(), | |
| ) | |
| # return self.SendScintilla(EM_EXLINEFROMCHAR, charPos) | |
| # EM_EXLINEFROMCHAR puts charPos in lParam, not wParam | |
| return self.SendScintilla(EM_EXLINEFROMCHAR, 0, charPos) | |
| def LineIndex(self, line): | |
| return self.SendScintilla(win32con.EM_LINEINDEX, line) | |
| def ScrollCaret(self): | |
| return self.SendScintilla(win32con.EM_SCROLLCARET) | |
| def GetCurLineNumber(self): | |
| return self.LineFromChar(self.SCIGetCurrentPos()) | |
| def GetTextLength(self): | |
| return self.SendScintilla(scintillacon.SCI_GETTEXTLENGTH) | |
| def GetTextRange(self, start=0, end=-1, decode=True): | |
| if end == -1: | |
| end = self.SendScintilla(scintillacon.SCI_GETTEXTLENGTH) | |
| assert end >= start, "Negative index requested (%d/%d)" % (start, end) | |
| assert ( | |
| start >= 0 and start <= self.GetTextLength() | |
| ), "The start postion is invalid" | |
| assert end >= 0 and end <= self.GetTextLength(), "The end postion is invalid" | |
| initer = null_byte * (end - start + 1) | |
| buff = array.array("b", initer) | |
| addressBuffer = buff.buffer_info()[0] | |
| tr = struct.pack("llP", start, end, addressBuffer) | |
| trBuff = array.array("b", tr) | |
| addressTrBuff = trBuff.buffer_info()[0] | |
| num_bytes = self.SendScintilla(EM_GETTEXTRANGE, 0, addressTrBuff) | |
| ret = buff.tobytes()[:num_bytes] | |
| if decode: | |
| ret = ret.decode(default_scintilla_encoding) | |
| return ret | |
| def ReplaceSel(self, str): | |
| buff = (str + "\0").encode(default_scintilla_encoding) | |
| self.SendScintilla(scintillacon.SCI_REPLACESEL, 0, buff) | |
| def GetLine(self, line=-1): | |
| if line == -1: | |
| line = self.GetCurLineNumber() | |
| start = self.LineIndex(line) | |
| end = self.LineIndex(line + 1) | |
| return self.GetTextRange(start, end) | |
| def SetReadOnly(self, flag=1): | |
| return self.SendScintilla(win32con.EM_SETREADONLY, flag) | |
| def LineScroll(self, lines, cols=0): | |
| return self.SendScintilla(win32con.EM_LINESCROLL, cols, lines) | |
| def GetFirstVisibleLine(self): | |
| return self.SendScintilla(win32con.EM_GETFIRSTVISIBLELINE) | |
| def SetWordWrap(self, mode): | |
| if mode != win32ui.CRichEditView_WrapNone: | |
| raise ValueError("We dont support word-wrap (I dont think :-)") | |
| class CScintillaColorEditInterface(CScintillaEditInterface): | |
| ################################ | |
| # Plug-in colorizer support | |
| def _GetColorizer(self): | |
| if not hasattr(self, "colorizer"): | |
| self.colorizer = self._MakeColorizer() | |
| return self.colorizer | |
| def _MakeColorizer(self): | |
| # Give parent a chance to hook. | |
| parent_func = getattr(self.GetParentFrame(), "_MakeColorizer", None) | |
| if parent_func is not None: | |
| return parent_func() | |
| from . import formatter | |
| ## return formatter.PythonSourceFormatter(self) | |
| return formatter.BuiltinPythonSourceFormatter(self) | |
| def Colorize(self, start=0, end=-1): | |
| c = self._GetColorizer() | |
| if c is not None: | |
| c.Colorize(start, end) | |
| def ApplyFormattingStyles(self, bReload=1): | |
| c = self._GetColorizer() | |
| if c is not None: | |
| c.ApplyFormattingStyles(bReload) | |
| # The Parent window will normally hook | |
| def HookFormatter(self, parent=None): | |
| c = self._GetColorizer() | |
| if c is not None: # No need if we have no color! | |
| c.HookFormatter(parent) | |
| class CScintillaEdit(window.Wnd, CScintillaColorEditInterface): | |
| def __init__(self, wnd=None): | |
| if wnd is None: | |
| wnd = win32ui.CreateWnd() | |
| window.Wnd.__init__(self, wnd) | |
| def SendScintilla(self, msg, w=0, l=0): | |
| return self.SendMessage(msg, w, l) | |
| def CreateWindow(self, style, rect, parent, id): | |
| self._obj_.CreateWindow("Scintilla", "Scintilla", style, rect, parent, id, None) | |