Spaces:
Paused
Paused
# 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) | |