123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- import string
- import win32api
- import win32ui
- import win32con
- import sys
- from pywin.mfc.dialog import GetSimpleInput
- from pywin import default_scintilla_encoding
- wordchars = string.ascii_uppercase + string.ascii_lowercase + string.digits
- class TextError(Exception):
- pass
- class EmptyRange(Exception):
- pass
- def GetIDLEModule(module):
- try:
-
- modname = "pywin.idle." + module
- __import__(modname)
- except ImportError as details:
- msg = "The IDLE extension '%s' can not be located.\r\n\r\n" \
- "Please correct the installation and restart the" \
- " application.\r\n\r\n%s" % (module, details)
- win32ui.MessageBox(msg)
- return None
- mod=sys.modules[modname]
- mod.TclError = TextError
- return mod
- def fast_readline(self):
- if self.finished:
- val = ""
- else:
- if "_scint_lines" not in self.__dict__:
-
- self._scint_lines = self.text.edit.GetTextRange().split("\n")
- sl = self._scint_lines
- i = self.i = self.i + 1
- if i >= len(sl):
- val = ""
- else:
- val = sl[i]+"\n"
- return val.encode(default_scintilla_encoding)
- try:
- GetIDLEModule("AutoIndent").IndentSearcher.readline = fast_readline
- except AttributeError:
- pass
- class IDLEEditorWindow:
- def __init__(self, edit):
- self.edit = edit
- self.text = TkText(edit)
- self.extensions = {}
- self.extension_menus = {}
- def close(self):
- self.edit = self.text = None
- self.extension_menus = None
- try:
- for ext in self.extensions.values():
- closer = getattr(ext, "close", None)
- if closer is not None:
- closer()
- finally:
- self.extensions = {}
- def IDLEExtension(self, extension):
- ext = self.extensions.get(extension)
- if ext is not None: return ext
- mod = GetIDLEModule(extension)
- if mod is None: return None
- klass = getattr(mod, extension)
- ext = self.extensions[extension] = klass(self)
-
- events = [item for item in dir(klass) if item[-6:]=="_event"]
- for event in events:
- name = "<<%s>>" % (event[:-6].replace("_", "-"), )
- self.edit.bindings.bind(name, getattr(ext, event))
- return ext
- def GetMenuItems(self, menu_name):
-
- bindings = self.edit.bindings
- ret = []
- for ext in self.extensions.values():
- menudefs = getattr(ext, "menudefs", [])
- for name, items in menudefs:
- if name == menu_name:
- for text, event in [item for item in items if item is not None]:
- text = text.replace("&", "&&")
- text = text.replace("_", "&")
- ret.append((text, event))
- return ret
-
-
-
- def askinteger(self, caption, prompt, parent=None, initialvalue=0, minvalue=None, maxvalue=None):
- while 1:
- rc = GetSimpleInput(prompt, str(initialvalue), caption)
- if rc is None: return 0
- err = None
- try:
- rc = int(rc)
- except ValueError:
- err = "Please enter an integer"
- if not err and minvalue is not None and rc < minvalue:
- err = "Please enter an integer greater then or equal to %s" % (minvalue,)
- if not err and maxvalue is not None and rc > maxvalue:
- err = "Please enter an integer less then or equal to %s" % (maxvalue,)
- if err:
- win32ui.MessageBox(err, caption, win32con.MB_OK)
- continue
- return rc
- def askyesno(self, caption, prompt, parent=None):
- return win32ui.MessageBox(prompt, caption, win32con.MB_YESNO)==win32con.IDYES
-
-
-
-
-
- def is_char_in_string(self, text_index):
-
-
-
- text_index = self.text._getoffset(text_index)
- c = self.text.edit._GetColorizer()
- if c and c.GetStringStyle(text_index) is None:
- return 0
- return 1
-
-
-
- def get_selection_indices(self):
- try:
- first = self.text.index("sel.first")
- last = self.text.index("sel.last")
- return first, last
- except TextError:
- return None, None
- def set_tabwidth(self, width ):
- self.edit.SCISetTabWidth(width)
- def get_tabwidth(self):
- return self.edit.GetTabWidth()
- class CallTips:
- def __init__(self, edit):
- self.edit = edit
- def showtip(self, tip_text):
- self.edit.SCICallTipShow(tip_text)
- def hidetip(self):
- self.edit.SCICallTipCancel()
- def TkOffsetToIndex(offset, edit):
- lineoff = 0
-
- offset = min(offset, edit.GetTextLength())
- line = edit.LineFromChar(offset)
- lineIndex = edit.LineIndex(line)
- return "%d.%d" % (line+1, offset-lineIndex)
- def _NextTok(str, pos):
-
- end = len(str)
- if pos>=end: return None, 0
- while pos < end and str[pos] in string.whitespace:
- pos = pos + 1
-
- if str[pos] in '+-':
- return str[pos],pos+1
-
- endPos = pos
- while endPos < end and str[endPos] in string.digits+".":
- endPos = endPos + 1
- if pos!=endPos: return str[pos:endPos], endPos
- endPos = pos
- while endPos < end and str[endPos] not in string.whitespace + string.digits + "+-":
- endPos = endPos + 1
- if pos!=endPos: return str[pos:endPos], endPos
- return None, 0
- def TkIndexToOffset(bm, edit, marks):
- base, nextTokPos = _NextTok(bm, 0)
- if base is None: raise ValueError("Empty bookmark ID!")
- if base.find(".")>0:
- try:
- line, col = base.split(".", 2)
- if col=="first" or col=="last":
-
- if line != "sel": raise ValueError("Tags arent here!")
- sel = edit.GetSel()
- if sel[0]==sel[1]:
- raise EmptyRange
- if col=="first":
- pos = sel[0]
- else:
- pos = sel[1]
- else:
-
- line = int(line)-1
- if line > edit.GetLineCount():
- pos = edit.GetTextLength()+1
- else:
- pos = edit.LineIndex(line)
- if pos==-1: pos = edit.GetTextLength()
- pos = pos + int(col)
- except (ValueError, IndexError):
- raise ValueError("Unexpected literal in '%s'" % base)
- elif base == 'insert':
- pos = edit.GetSel()[0]
- elif base=='end':
- pos = edit.GetTextLength()
-
- if pos and edit.SCIGetCharAt(pos-1) != "\n":
- pos = pos+1
- else:
- try:
- pos = marks[base]
- except KeyError:
- raise ValueError("Unsupported base offset or undefined mark '%s'" % base)
- while 1:
- word, nextTokPos = _NextTok(bm, nextTokPos)
- if word is None: break
- if word in ['+','-']:
- num, nextTokPos = _NextTok(bm, nextTokPos)
- if num is None: raise ValueError("+/- operator needs 2 args")
- what, nextTokPos = _NextTok(bm, nextTokPos)
- if what is None: raise ValueError("+/- operator needs 2 args")
- if what[0] != "c": raise ValueError("+/- only supports chars")
- if word=='+':
- pos = pos + int(num)
- else:
- pos = pos - int(num)
- elif word=='wordstart':
- while pos > 0 and edit.SCIGetCharAt(pos-1) in wordchars:
- pos = pos - 1
- elif word=='wordend':
- end = edit.GetTextLength()
- while pos < end and edit.SCIGetCharAt(pos) in wordchars:
- pos = pos + 1
- elif word=='linestart':
- while pos > 0 and edit.SCIGetCharAt(pos-1) not in '\n\r':
- pos = pos - 1
- elif word=='lineend':
- end = edit.GetTextLength()
- while pos < end and edit.SCIGetCharAt(pos) not in '\n\r':
- pos = pos + 1
- else:
- raise ValueError("Unsupported relative offset '%s'" % word)
- return max(pos, 0)
- class TkText:
- def __init__(self, edit):
- self.calltips = None
- self.edit = edit
- self.marks = {}
- def make_calltip_window(self):
- if self.calltips is None:
- self.calltips = CallTips(self.edit)
- return self.calltips
- def _getoffset(self, index):
- return TkIndexToOffset(index, self.edit, self.marks)
- def _getindex(self, off):
- return TkOffsetToIndex(off, self.edit)
- def _fix_indexes(self, start, end):
-
- while start > 0 and ord(self.edit.SCIGetCharAt(start)) & 0xC0 == 0x80:
- start -= 1
- while end < self.edit.GetTextLength() and ord(self.edit.SCIGetCharAt(end)) & 0xC0 == 0x80:
- end += 1
-
- if start>0 and self.edit.SCIGetCharAt(start)=='\n' and self.edit.SCIGetCharAt(start-1)=='\r':
- start = start - 1
- if end < self.edit.GetTextLength() and self.edit.SCIGetCharAt(end-1)=='\r' and self.edit.SCIGetCharAt(end)=='\n':
- end = end + 1
- return start, end
- def bind(self, binding, handler):
- self.edit.bindings.bind(binding, handler)
- def get(self, start, end = None):
- try:
- start = self._getoffset(start)
- if end is None:
- end = start+1
- else:
- end = self._getoffset(end)
- except EmptyRange:
- return ""
-
- if end <= start: return ""
- max = self.edit.GetTextLength()
- checkEnd = 0
- if end > max:
- end = max
- checkEnd = 1
- start, end = self._fix_indexes(start, end)
- ret = self.edit.GetTextRange(start, end)
-
- if checkEnd and (not ret or ret[-1] != '\n'): ret = ret + '\n'
- return ret.replace("\r", "")
- def index(self, spec):
- try:
- return self._getindex(self._getoffset(spec))
- except EmptyRange:
- return ""
- def insert(self, pos, text):
- try:
- pos = self._getoffset(pos)
- except EmptyRange:
- raise TextError("Empty range")
- self.edit.SetSel((pos, pos))
-
- bits = text.split('\n')
- self.edit.SCIAddText(bits[0])
- for bit in bits[1:]:
- self.edit.SCINewline()
- self.edit.SCIAddText(bit)
- def delete(self, start, end=None):
- try:
- start = self._getoffset(start)
- if end is not None: end = self._getoffset(end)
- except EmptyRange:
- raise TextError("Empty range")
-
- if start==end: return
-
- if end is None:
- end = start+1
- else:
-
- if end<start: return
- if start==self.edit.GetTextLength(): return
- old = self.edit.GetSel()[0]
-
- start, end = self._fix_indexes(start, end)
- self.edit.SetSel((start, end))
- self.edit.Clear()
- if old>=start and old<end:
- old=start
- elif old>=end:
- old = old - (end-start)
- self.edit.SetSel(old)
- def bell(self):
- win32api.MessageBeep()
- def see(self, pos):
-
-
- pass
- def mark_set(self, name, pos):
- try:
- pos = self._getoffset(pos)
- except EmptyRange:
- raise TextError("Empty range '%s'" % pos)
- if name == "insert":
- self.edit.SetSel( pos )
- else:
- self.marks[name]=pos
- def tag_add(self, name, start, end):
- if name != "sel": raise ValueError("Only sel tag is supported")
- try:
- start = self._getoffset(start)
- end = self._getoffset(end)
- except EmptyRange:
- raise TextError("Empty range")
- self.edit.SetSel( start, end )
- def tag_remove(self, name, start, end):
- if name !="sel" or start != "1.0" or end != "end":
- raise ValueError("Cant remove this tag")
-
- self.edit.SetSel(self.edit.GetSel()[0])
- def compare(self, i1, op, i2):
- try:
- i1=self._getoffset(i1)
- except EmptyRange:
- i1 = ""
- try:
- i2=self._getoffset(i2)
- except EmptyRange:
- i2 = ""
- return eval("%d%s%d" % (i1,op,i2))
- def undo_block_start(self):
- self.edit.SCIBeginUndoAction()
- def undo_block_stop(self):
- self.edit.SCIEndUndoAction()
- def TestCheck(index, edit, expected=None):
- rc = TkIndexToOffset(index, edit, {})
- if rc != expected:
- print("ERROR: Index", index,", expected", expected, "but got", rc)
- def TestGet(fr, to, t, expected):
- got = t.get(fr, to)
- if got != expected:
- print("ERROR: get(%s, %s) expected %s, but got %s" % (repr(fr), repr(to), repr(expected), repr(got)))
- def test():
- import pywin.framework.editor
- d=pywin.framework.editor.editorTemplate.OpenDocumentFile(None)
- e=d.GetFirstView()
- t = TkText(e)
- e.SCIAddText("hi there how\nare you today\r\nI hope you are well")
- e.SetSel((4,4))
- skip = """
- TestCheck("insert", e, 4)
- TestCheck("insert wordstart", e, 3)
- TestCheck("insert wordend", e, 8)
- TestCheck("insert linestart", e, 0)
- TestCheck("insert lineend", e, 12)
- TestCheck("insert + 4 chars", e, 8)
- TestCheck("insert +4c", e, 8)
- TestCheck("insert - 2 chars", e, 2)
- TestCheck("insert -2c", e, 2)
- TestCheck("insert-2c", e, 2)
- TestCheck("insert-2 c", e, 2)
- TestCheck("insert- 2c", e, 2)
- TestCheck("1.1", e, 1)
- TestCheck("1.0", e, 0)
- TestCheck("2.0", e, 13)
- try:
- TestCheck("sel.first", e, 0)
- print "*** sel.first worked with an empty selection"
- except TextError:
- pass
- e.SetSel((4,5))
- TestCheck("sel.first- 2c", e, 2)
- TestCheck("sel.last- 2c", e, 3)
- """
-
- e.SetSel((4,4))
- TestGet("insert lineend", "insert lineend +1c", t, "\n")
- e.SetSel((20, 20))
- TestGet("insert lineend", "insert lineend +1c", t, "\n")
- e.SetSel((35, 35))
- TestGet("insert lineend", "insert lineend +1c", t, "\n")
- class IDLEWrapper:
- def __init__(self, control):
- self.text = control
- def IDLETest(extension):
- import sys, os
- modname = "pywin.idle." + extension
- __import__(modname)
- mod=sys.modules[modname]
- mod.TclError = TextError
- klass = getattr(mod, extension)
-
- import pywin.framework.editor
- d=pywin.framework.editor.editorTemplate.OpenDocumentFile(None)
- v=d.GetFirstView()
- fname=os.path.splitext(__file__)[0] + ".py"
- v.SCIAddText(open(fname).read())
- d.SetModifiedFlag(0)
- r=klass( IDLEWrapper( TkText(v) ) )
- return r
- if __name__=='__main__':
- test()
-
|