Kano001's picture
Upload 498 files
d12bc25 verified
raw
history blame
20.5 kB
# 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)