IDLEenvironment.py :  » Windows » pyExcelerator » pywin32-214 » pythonwin » pywin » scintilla » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Windows » pyExcelerator 
pyExcelerator » pywin32 214 » pythonwin » pywin » scintilla » IDLEenvironment.py
# Code that allows Pythonwin to pretend it is IDLE
# (at least as far as most IDLE extensions are concerned)

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): # When a TclError would normally be raised.
  pass


class EmptyRange(Exception): # Internally raised.
  pass

def GetIDLEModule(module):
  try:
    # First get it from Pythonwin it is exists.
    modname = "pywin.idle." + module
    __import__(modname)
  except ImportError, 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 # A hack that can go soon!
  return mod

# A class that is injected into the IDLE auto-indent extension.
# It allows for decent performance when opening a new file,
# as auto-indent uses the tokenizer module to determine indents.
# The default AutoIndent readline method works OK, but it goes through
# this layer of Tk index indirection for every single line.  For large files
# without indents (and even small files with indents :-) it was pretty slow!
def fast_readline(self):
  if self.finished:
    val = ""
  else:
    if "_scint_lines" not in self.__dict__:
      # XXX - note - assumes this is only called once the file is loaded!
      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: # GetIDLEModule may return None
  pass

# A class that attempts to emulate an IDLE editor window.
# Construct with a Pythonwin view.
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.itervalues():
        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)
    # Find and bind all the events defined in the extension.
    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):
    # Get all menu items for the menu name (eg, "edit")
    bindings = self.edit.bindings
    ret = []
    for ext in self.extensions.itervalues():
      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

  ######################################################################
  # The IDLE "Virtual UI" methods that are exposed to the IDLE extensions.
  #
  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 # Correct "cancel" semantics?
      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

  ######################################################################
  # The IDLE "Virtual Text Widget" methods that are exposed to the IDLE extensions.
  #

  # Is character at text_index in a Python string?  Return 0 for
  # "guaranteed no", true for anything else.
  def is_char_in_string(self, text_index):
    # A helper for the code analyser - we need internal knowledge of
    # the colorizer to get this information
    # This assumes the colorizer has got to this point!
    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

  # If a selection is defined in the text widget, return
  # (start, end) as Tkinter text indices, otherwise return
  # (None, None)
  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()

# A class providing the generic "Call Tips" interface
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()

########################################
#
# Helpers for the TkText emulation.
def TkOffsetToIndex(offset, edit):
  lineoff = 0
  # May be 1 > actual end if we pretended there was a trailing '\n'
  offset = min(offset, edit.GetTextLength())
  line = edit.LineFromChar(offset)
  lineIndex = edit.LineIndex(line)
  return "%d.%d" % (line+1, offset-lineIndex)

def _NextTok(str, pos):
  # Returns (token, endPos)
  end = len(str)
  if pos>=end: return None, 0
  while pos < end and str[pos] in string.whitespace:
    pos = pos + 1
  # Special case for +-
  if str[pos] in '+-':
    return str[pos],pos+1
  # Digits also a special case.
  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":
        # Tag name
        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:
        # Lines are 1 based for tkinter
        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()
    # Pretend there is a trailing '\n' if necessary
    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) # Tkinter is tollerant of -ve indexes - we aren't

# A class that resembles an IDLE (ie, a Tk) text widget.
# Construct with an edit object (eg, an editor view)
class TkText:
  def __init__(self, edit):
    self.calltips = None
    self.edit = edit
    self.marks = {}
##  def __getattr__(self, attr):
##    if attr=="tk": return self # So text.tk.call works.
##    if attr=="master": return None # ditto!
##    raise AttributeError, attr
##  def __getitem__(self, item):
##    if item=="tabs":
##      size = self.edit.GetTabWidth()
##      if size==8: return "" # Tk default
##      return size # correct semantics?
##    elif item=="font": # Used for measurements we dont need to do!
##      return "Dont know the font"
##    raise IndexError, "Invalid index '%s'" % item
  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):
    # first some magic to handle skipping over utf8 extended chars.
    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
    # now handling fixing \r\n->\n disparities...
    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 get_tab_width(self):
##    return self.edit.GetTabWidth()
##  def call(self, *rest):
##    # Crap to support Tk measurement hacks for tab widths
##    if rest[0] != "font" or rest[1] != "measure":
##      raise ValueError, "Unsupport call type"
##    return len(rest[5])
##  def configure(self, **kw):
##    for name, val in kw.items():
##      if name=="tabs":
##        self.edit.SCISetTabWidth(int(val))
##      else:
##        raise ValueError, "Unsupported configuration item %s" % kw
  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 ""
    # Simple semantic checks to conform to the Tk text interface
    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)
    # pretend a trailing '\n' exists if necessary.
    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))
    # IDLE only deals with "\n" - we will be nicer

    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 end is specified and == start, then we must delete nothing.
    if start==end: return
    # If end is not specified, delete one char
    if end is None:
      end = start+1
    else:
      # Tk says not to delete in this case, but our control would.
      if end<start: return
    if start==self.edit.GetTextLength(): return # Nothing to delete.
    old = self.edit.GetSel()[0] # Lose a selection
    # Hack for partial '\r\n' and UTF-8 char removal
    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):
    # Most commands we use in Scintilla actually force the selection
    # to be seen, making this unnecessary.
    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")
    # Turn the sel into a cursor
    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()

######################################################################
# 
# Test related code.
# 
######################################################################
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)
  """
  # Check EOL semantics
  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)

  # Create a new Scintilla Window.
  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()
        
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.