Beispiel #1
0
 def print_window(self, event):
     m = tkMessageBox.Message(title="Print",
                              message="Print to Default Printer",
                              icon=tkMessageBox.QUESTION,
                              type=tkMessageBox.OKCANCEL,
                              default=tkMessageBox.OK,
                              master=self.text)
     reply = m.show()
     if reply != tkMessageBox.OK:
         self.text.focus_set()
         return "break"
     tempfilename = None
     saved = self.get_saved()
     if saved:
         filename = self.filename
     # shell undo is reset after every prompt, looks saved, probably isn't
     if not saved or filename is None:
         (tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_')
         filename = tempfilename
         os.close(tfd)
         if not self.writefile(tempfilename):
             os.unlink(tempfilename)
             return "break"
     platform = os.name
     printPlatform = 1
     if platform == 'posix':  #posix platform
         command = idleConf.GetOption('main', 'General',
                                      'print-command-posix')
         command = command + " 2>&1"
     elif platform == 'nt':  #win32 platform
         command = idleConf.GetOption('main', 'General',
                                      'print-command-win')
     else:  #no printing for this platform
         printPlatform = 0
     if printPlatform:  #we can try to print for this platform
         command = command % filename
         pipe = os.popen(command, "r")
         # things can get ugly on NT if there is no printer available.
         output = pipe.read().strip()
         status = pipe.close()
         if status:
             output = "Printing failed (exit status 0x%x)\n" % \
                      status + output
         if output:
             output = "Printing command: %s\n" % repr(command) + output
             tkMessageBox.showerror("Print status",
                                    output,
                                    master=self.text)
     else:  #no printing for this platform
         message = "Printing is not enabled for this platform: %s" % platform
         tkMessageBox.showinfo("Print status", message, master=self.text)
     if tempfilename:
         os.unlink(tempfilename)
     return "break"
    def ResetFont(self):
        "Update the text widgets' font if it is changed"
        # Called from configDialog.py
        fontWeight = 'normal'
        if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'):
            fontWeight = 'bold'

        for page in self.text_notebook.pages.itervalues():
            text = page.editpage.text
            text.config(
                font=(idleConf.GetOption('main', 'EditorPage', 'font'),
                      idleConf.GetOption('main', 'EditorPage', 'font-size'),
                      fontWeight))
Beispiel #3
0
    def getfilename(self):
        """Get source filename.  If not saved, offer to save (or create) file

        The debugger requires a source file.  Make sure there is one, and that
        the current version of the source buffer has been saved.  If the user
        declines to save or cancels the Save As dialog, return None.

        If the user has configured IDLE for Autosave, the file will be
        silently saved if it already exists and is dirty.

        """
        filename = self.editwin.io.filename
        is_temp = False
        if not self.editwin.get_saved():
            autosave = idleConf.GetOption('main',
                                          'General',
                                          'autosave',
                                          type='bool')
            save_before_run = idleConf.GetOption('main',
                                                 'General',
                                                 'save-before-run',
                                                 default=1,
                                                 type='bool')

            io = self.editwin.io
            if filename and autosave:
                # This has been saved before and IDLE is configured to not
                # prompt before saving this file again.
                io.save(None)
            elif filename or save_before_run:
                # This has been saved before and IDLE is configured to prompt
                # before saving this file again, or, this has never been
                # saved and the configuration tells it must be saved.
                reply = self.ask_save_dialog()
                self.editwin.text.focus_set()
                if reply == "ok":
                    io.save(None)
                    filename = self.editwin.io.filename
                else:
                    filename = None
            else:
                # This has never been saved before but IDLE is configured
                # to allow running the present code without explicitly
                # saving.
                if not save_before_run:
                    filename = io.save_as_temp(prefix='IDLE_rtmp_')
                    is_temp = True

        return filename, is_temp
Beispiel #4
0
 def drawtext(self):
     textx = self.x+20-1
     texty = self.y-1
     labeltext = self.item.GetLabelText()
     if labeltext:
         id = self.canvas.create_text(textx, texty, anchor="nw",
                                      text=labeltext)
         self.canvas.tag_bind(id, "<1>", self.select)
         self.canvas.tag_bind(id, "<Double-1>", self.flip)
         x0, y0, x1, y1 = self.canvas.bbox(id)
         textx = max(x1, 200) + 10
     text = self.item.GetText() or "<no text>"
     try:
         self.entry
     except AttributeError:
         pass
     else:
         self.edit_finish()
     try:
         label = self.label
     except AttributeError:
         # padding carefully selected (on Windows) to match Entry widget:
         self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2)
     theme = idleConf.GetOption('main','Theme','name')
     if self.selected:
         self.label.configure(idleConf.GetHighlight(theme, 'hilite'))
     else:
         self.label.configure(idleConf.GetHighlight(theme, 'normal'))
     id = self.canvas.create_window(textx, texty,
                                    anchor="nw", window=self.label)
     self.label.bind("<1>", self.select_or_edit)
     self.label.bind("<Double-1>", self.flip)
     self.text_id = id
    def getfilename(self):
        """Get source filename.  If not saved, offer to save (or create) file

        The debugger requires a source file.  Make sure there is one, and that
        the current version of the source buffer has been saved.  If the user
        declines to save or cancels the Save As dialog, return None.

        If the user has configured IDLE for Autosave, the file will be
        silently saved if it already exists and is dirty.
        """
        page = self.editpage
        filename = page.io.filename
        if not page.get_saved():
            autosave = idleConf.GetOption('main',
                                          'General',
                                          'autosave',
                                          type='bool')
            if autosave and filename:
                page.io.save(None)
            else:
                reply = self.ask_save_dialog()
                page.text.focus_set()
                if reply == "ok":
                    page.io.save(None)
                    filename = page.io.filename
                else:
                    filename = None
        return filename
 def set_notabs_indentwidth(self):
     "Update the indentwidth if changed and not using tabs in this window"
     # Called from configDialog.py
     if not self.usetabs:
         self.indentwidth = idleConf.GetOption('main',
                                               'Indent',
                                               'num-spaces',
                                               type='int')
Beispiel #7
0
 def __init__(self, text, output_sep="\n"):
     self.text = text
     self.history = []
     self.history_prefix = None
     self.history_pointer = None
     self.output_sep = output_sep
     self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1,
                                      "bool")
     text.bind("<<history-previous>>", self.history_prev)
     text.bind("<<history-next>>", self.history_next)
 def __new__(cls, name, bases, dct):
     newcls = type.__new__(cls, name, bases, dct)
     if name != 'EnablableExtension':
         idleConf.GetOption("extensions",
                            name,
                            "enable",
                            type="bool",
                            default=True,
                            member_name='enable')
     return newcls
Beispiel #9
0
    def __init__(self, parent, filename):
        "Configure tags and feed file to parser."
        uwide = idleConf.GetOption('main', 'EditorWindow', 'width', type='int')
        uhigh = idleConf.GetOption('main',
                                   'EditorWindow',
                                   'height',
                                   type='int')
        uhigh = 3 * uhigh // 4  # lines average 4/3 of editor line height
        Text.__init__(self,
                      parent,
                      wrap='word',
                      highlightthickness=0,
                      padx=5,
                      borderwidth=0,
                      width=uwide,
                      height=uhigh)

        normalfont = self.findfont(['TkDefaultFont', 'arial', 'helvetica'])
        fixedfont = self.findfont(['TkFixedFont', 'monaco', 'courier'])
        self['font'] = (normalfont, 12)
        self.tag_configure('em', font=(normalfont, 12, 'italic'))
        self.tag_configure('h1', font=(normalfont, 20, 'bold'))
        self.tag_configure('h2', font=(normalfont, 18, 'bold'))
        self.tag_configure('h3', font=(normalfont, 15, 'bold'))
        self.tag_configure('pre', font=(fixedfont, 12), background='#f6f6ff')
        self.tag_configure('preblock',
                           font=(fixedfont, 10),
                           lmargin1=25,
                           borderwidth=1,
                           relief='solid',
                           background='#eeffcc')
        self.tag_configure('l1', lmargin1=25, lmargin2=25)
        self.tag_configure('l2', lmargin1=50, lmargin2=50)
        self.tag_configure('l3', lmargin1=75, lmargin2=75)
        self.tag_configure('l4', lmargin1=100, lmargin2=100)

        self.parser = HelpParser(self)
        with open(filename, encoding='utf-8') as f:
            contents = f.read()
        self.parser.feed(contents)
        self['state'] = 'disabled'
Beispiel #10
0
 def LoadTagDefs(self):
     ColorDelegator.LoadTagDefs(self)
     theme = idleConf.GetOption('main', 'Theme', 'name')
     self.tagdefs.update({
         "stdin": {
             'background': None,
             'foreground': None
         },
         "stdout": idleConf.GetHighlight(theme, "stdout"),
         "stderr": idleConf.GetHighlight(theme, "stderr"),
         "console": idleConf.GetHighlight(theme, "console"),
     })
Beispiel #11
0
    def __init__(self, parent_frame, editwin, title=None, **kwargs):
        self.editwin = editwin
        self.title = title
        self.tab_initialized = False
        kwargs.setdefault('width',
                          idleConf.GetOption('main', 'EditorPage', 'width'))
        kwargs.setdefault('height',
                          idleConf.GetOption('main', 'EditorPage', 'height'))

        self.text = MultiCallCreator(Text)(parent_frame, **kwargs)
        self.color = None  # initialized in reset_colorizer
        self.per = Percolator(self.text)
        self.undo = self.editwin.UndoDelegator()
        self.per.insertfilter(self.undo)
        self.text.undo_block_start = self.undo.undo_block_start
        self.text.undo_block_stop = self.undo.undo_block_stop
        self.io = IOBinding.IOBinding(self)

        self.undo.set_saved_change_hook(self.saved_change_hook)
        self.io.set_filename_change_hook(self.filename_change_hook)
        self.reset_colorizer()
        self._setup_bindings()
Beispiel #12
0
 def LoadTagDefs(self):
     theme = idleConf.GetOption('main','Theme','name')
     self.tagdefs = {
         "COMMENT": idleConf.GetHighlight(theme, "comment"),
         "KEYWORD": idleConf.GetHighlight(theme, "keyword"),
         "STRING": idleConf.GetHighlight(theme, "string"),
         "DEFINITION": idleConf.GetHighlight(theme, "definition"),
         "SYNC": {'background':None,'foreground':None},
         "TODO": {'background':None,'foreground':None},
         "BREAK": idleConf.GetHighlight(theme, "break"),
         "ERROR": idleConf.GetHighlight(theme, "error"),
         # The following is used by ReplaceDialog:
         "hit": idleConf.GetHighlight(theme, "hit"),
         }
Beispiel #13
0
    def reset_colorizer(self):
        "Update the colour theme"
        # Called from self.filename_change_hook and from configDialog.py
        self.__rmcolorizer()
        self.__addcolorizer()
        theme = idleConf.GetOption('main', 'Theme', 'name')
        normal_colors = idleConf.GetHighlight(theme, 'normal')
        cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
        select_colors = idleConf.GetHighlight(theme, 'hilite')

        self.text.config(foreground=normal_colors['foreground'],
                         background=normal_colors['background'],
                         insertbackground=cursor_color,
                         selectforeground=select_colors['foreground'],
                         selectbackground=select_colors['background'])
Beispiel #14
0
 def remote_stack_viewer(self):
     import RemoteObjectBrowser
     oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist", ), {})
     if oid is None:
         self.tkconsole.root.bell()
         return
     item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid)
     from TreeWidget import ScrolledCanvas, TreeNode
     top = Toplevel(self.tkconsole.root)
     theme = idleConf.GetOption('main', 'Theme', 'name')
     background = idleConf.GetHighlight(theme, 'normal')['background']
     sc = ScrolledCanvas(top, bg=background, highlightthickness=0)
     sc.frame.pack(expand=1, fill="both")
     node = TreeNode(sc.canvas, None, item)
     node.expand()
Beispiel #15
0
 def format_paragraph_event(self, event):
     maxformatwidth = int(idleConf.GetOption('main', 'FormatParagraph',
         'paragraph'))
     text = self.editpage.text
     first, last = self.editpage.get_selection_indices()
     if first and last:
         data = text.get(first, last)
         comment_header = ''
     else:
         first, last, comment_header, data = find_paragraph(text,
             text.index("insert"))
     if comment_header:
         # Reformat the comment lines - convert to text sans header.
         lines = data.split("\n")
         lines = map(lambda st, l=len(comment_header): st[l:], lines)
         data = "\n".join(lines)
         # Reformat to maxformatwidth chars or a 20 char width, whichever is
         # greater.
         format_width = max(maxformatwidth - len(comment_header), 20)
         newdata = reformat_paragraph(data, format_width)
         # re-split and re-insert the comment header.
         newdata = newdata.split("\n")
         # If the block ends in a \n, we dont want the comment
         # prefix inserted after it. (Im not sure it makes sense to
         # reformat a comment block that isnt made of complete
         # lines, but whatever!)  Can't think of a clean soltution,
         # so we hack away
         block_suffix = ""
         if not newdata[-1]:
             block_suffix = "\n"
             newdata = newdata[:-1]
         builder = lambda item, prefix=comment_header: prefix+item
         newdata = '\n'.join(map(builder, newdata)) + block_suffix
     else:
         # Just a normal text format
         newdata = reformat_paragraph(data, maxformatwidth)
     text.tag_remove("sel", "1.0", "end")
     if newdata != data:
         text.mark_set("insert", first)
         text.undo_block_start()
         text.delete(first, last)
         text.insert(first, newdata)
         text.undo_block_stop()
     else:
         text.mark_set("insert", last)
     text.see("insert")
     return "break"
Beispiel #16
0
    def open(self, event=None, editFile=None):
        if self.editwin.flist:
            if not editFile:
                filename = self.askopenfile()
            else:
                filename = editFile
            if filename:
                # If the current window has no filename and hasn't been
                # modified, we replace its contents (no loss). Otherwise
                # we open a new window, or maybe open in a tab.
                # But we won't replace the shell window (which has an
                # interp(reter) attribute), which gets set to "not modified"
                # at every new prompt.
                try:
                    interp = self.editwin.interp
                except AttributeError:
                    interp = None

                if not self.filename and self.get_saved() and not interp:
                    self.editwin.flist.open(filename, self.loadfile)
                else:
                    if idleConf.GetOption('main',
                                          'EditorWindow',
                                          'file-in-tab',
                                          default=1,
                                          type='bool'):
                        self.editwin.flist.open(filename)
            else:
                self.text.focus_set()
            return "break"
        #
        # Code for use outside IDLE:
        if self.get_saved():
            reply = self.maybesave()
            if reply == "cancel":
                self.text.focus_set()
                return "break"
        if not editFile:
            filename = self.askopenfile()
        else:
            filename = editFile
        if filename:
            self.loadfile(filename)
        else:
            self.text.focus_set()
        return "break"
Beispiel #17
0
    def __init__(self, text):
        '''Initialize data attributes and bind event methods.

        .text - Idle wrapper of tk Text widget, with .bell().
        .history - source statements, possibly with multiple lines.
        .prefix - source already entered at prompt; filters history list.
        .pointer - index into history.
        .cyclic - wrap around history list (or not).
        '''
        self.text = text
        self.history = []
        self.prefix = None
        self.pointer = None
        self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1,
                                         "bool")
        text.bind("<<history-previous>>", self.history_prev)
        text.bind("<<history-next>>", self.history_next)
Beispiel #18
0
 def __init__(self, editwin):
     self.editwin = editwin
     self.text = editwin.text
     self.textfont = self.text["font"]
     self.label = None
     # Dummy line, which starts the "block" of the whole document:
     self.info = list(self.interesting_lines(1))
     self.lastfirstline = 1
     visible = idleConf.GetOption("extensions",
                                  "CodeContext",
                                  "visible",
                                  type="bool",
                                  default=False)
     if visible:
         self.toggle_code_context_event()
         self.editwin.setvar('<<toggle-code-context>>', True)
     # Start two update cycles, one for context lines, one for font changes.
     self.text.after(UPDATEINTERVAL, self.timer_event)
     self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
 def init(self, flist):
     self.flist = flist
     # reset pyclbr
     pyclbr._modules.clear()
     # create top
     self.top = top = ListedToplevel(flist.root)
     top.protocol("WM_DELETE_WINDOW", self.close)
     top.bind("<Escape>", self.close)
     self.settitle()
     top.focus_set()
     # create scrolled canvas
     theme = idleConf.GetOption('main','Theme','name')
     background = idleConf.GetHighlight(theme, 'normal')['background']
     sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1)
     sc.frame.pack(expand=1, fill="both")
     item = self.rootnode()
     self.node = node = TreeNode(sc.canvas, None, item)
     node.update()
     node.expand()
Beispiel #20
0
 def __init__(self, editpage):
     self.editpage = editpage
     self.editwin = editpage.editwin
     self.label = None
     # self.info is a list of (line number, indent level, line text, block
     # keyword) tuples providing the block structure associated with
     # self.topvisible (the linenumber of the line displayed at the top of
     # the edit window). self.info[0] is initialized as a 'dummy' line which
     # starts the toplevel 'block' of the module.
     self.info = [(0, -1, "", False)]
     self.topvisible = 1
     visible = idleConf.GetOption("extensions",
                                  "CodeContext",
                                  "visible",
                                  type="bool",
                                  default=False)
     if visible:
         self.toggle_code_context_event()
         self.editwin.setvar('<<toggle-code-context>>', True)
Beispiel #21
0
 def __init__(self, editwin):
     self.editwin = editwin
     self.text = editwin.text
     self.textfont = self.text["font"]
     self.label = None
     # self.info is a list of (line number, indent level, line text, block
     # keyword) tuples providing the block structure associated with
     # self.topvisible (the linenumber of the line displayed at the top of
     # the edit window). self.info[0] is initialized as a 'dummy' line which
     # starts the toplevel 'block' of the module.
     self.info = [(0, -1, "", False)]
     self.topvisible = 1
     visible = idleConf.GetOption("extensions", "CodeContext",
                                  "visible", type="bool", default=False)
     if visible:
         self.toggle_code_context_event()
         self.editwin.setvar('<<toggle-code-context>>', True)
     # Start two update cycles, one for context lines, one for font changes.
     self.text.after(UPDATEINTERVAL, self.timer_event)
     self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
Beispiel #22
0
 def build_subprocess_arglist(self):
     w = ['-W' + s for s in sys.warnoptions]
     # Maybe IDLE is installed and is being accessed via sys.path,
     # or maybe it's not installed and the idle.py script is being
     # run from the IDLE source directory.
     del_exitf = idleConf.GetOption('main',
                                    'General',
                                    'delete-exitfunc',
                                    default=False,
                                    type='bool')
     if __name__ == 'idlelib.PyShell':
         command = "__import__('idlelib.run').run.main(" + ` del_exitf ` + ")"
     else:
         command = "__import__('run').main(" + ` del_exitf ` + ")"
     if sys.platform == 'win32' and ' ' in sys.executable:
         # handle embedded space in path by quoting the argument
         decorated_exec = '"%s"' % sys.executable
     else:
         decorated_exec = sys.executable
     return [decorated_exec] + w + ["-c", command, str(self.port)]
Beispiel #23
0
    def open(self, filename, action=None):
        assert filename
        filename = _canonize(filename)
        if os.path.isdir(filename):
            # This can happen when bad filename is passed on command line:
            tkMessageBox.showerror("File Error",
                                   "%r is a directory." % (filename, ),
                                   master=self.root)
            return None
        key = os.path.normcase(filename)
        if self.dict.has_key(key):
            edit = self.dict[key]
            edit.top.wakeup()
            return edit

        if action:
            # Don't create window, perform 'action', e.g. open in same window
            return action(filename=filename)

        elif idleConf.GetOption('main',
                                'EditorWindow',
                                'file-in-tab',
                                default=1,
                                type='bool'):
            # check if there is some PyShellEditorWindow running to open this
            # new tab
            for entry in self.inversedict:
                if hasattr(entry, 'set_breakpoint'):
                    # PyShellEditorWindow, good
                    entry.new_tab(filename=filename)
                    # select the last page created
                    entry.text_notebook.select(
                        len(entry.text_notebook.pages) - 1)
                    return entry
            else:
                # no PyShellEditorWindows, create one
                return self.EditorWindow(self, filename, key)

        else:
            return self.EditorWindow(self, filename, key)
Beispiel #24
0
class AutoComplete:

    menudefs = [('edit', [
        ("Show Completions", "<<force-open-completions>>"),
    ])]

    popupwait = idleConf.GetOption("extensions",
                                   "AutoComplete",
                                   "popupwait",
                                   type="int",
                                   default=0)

    def __init__(self, editwin=None):
        if editwin == None:  # subprocess and test
            self.editwin = None
            return
        self.editwin = editwin
        self.text = editwin.text
        self.autocompletewindow = None

        # id of delayed call, and the index of the text insert when the delayed
        # call was issued. If _delayed_completion_id is None, there is no
        # delayed call.
        self._delayed_completion_id = None
        self._delayed_completion_index = None

    def _make_autocomplete_window(self):
        return AutoCompleteWindow.AutoCompleteWindow(self.text)

    def _remove_autocomplete_window(self, event=None):
        if self.autocompletewindow:
            self.autocompletewindow.hide_window()
            self.autocompletewindow = None

    def force_open_completions_event(self, event):
        """Happens when the user really wants to open a completion list, even
        if a function call is needed.
        """
        self.open_completions(True, False, True)

    def try_open_completions_event(self, event):
        """Happens when it would be nice to open a completion list, but not
        really neccesary, for example after an dot, so function
        calls won't be made.
        """
        lastchar = self.text.get("insert-1c")
        if lastchar == ".":
            self._open_completions_later(False, False, False,
                                         COMPLETE_ATTRIBUTES)
        elif lastchar == os.sep:
            self._open_completions_later(False, False, False, COMPLETE_FILES)

    def autocomplete_event(self, event):
        """Happens when the user wants to complete his word, and if neccesary,
        open a completion list after that (if there is more than one
        completion)
        """
        if hasattr(event, "mc_state") and event.mc_state:
            # A modifier was pressed along with the tab, continue as usual.
            return
        if self.autocompletewindow and self.autocompletewindow.is_active():
            self.autocompletewindow.complete()
            return "break"
        else:
            opened = self.open_completions(False, True, True)
            if opened:
                return "break"

    def _open_completions_later(self, *args):
        self._delayed_completion_index = self.text.index("insert")
        if self._delayed_completion_id is not None:
            self.text.after_cancel(self._delayed_completion_id)
        self._delayed_completion_id = \
            self.text.after(self.popupwait, self._delayed_open_completions,
                            *args)

    def _delayed_open_completions(self, *args):
        self._delayed_completion_id = None
        if self.text.index("insert") != self._delayed_completion_index:
            return
        self.open_completions(*args)

    def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
        """Find the completions and create the AutoCompleteWindow.
        Return True if successful (no syntax error or so found).
        if complete is True, then if there's nothing to complete and no
        start of completion, won't open completions and return False.
        If mode is given, will open a completion list only in this mode.
        """
        # Cancel another delayed call, if it exists.
        if self._delayed_completion_id is not None:
            self.text.after_cancel(self._delayed_completion_id)
            self._delayed_completion_id = None

        hp = HyperParser(self.editwin, "insert")
        curline = self.text.get("insert linestart", "insert")
        i = j = len(curline)
        if hp.is_in_string() and (not mode or mode == COMPLETE_FILES):
            self._remove_autocomplete_window()
            mode = COMPLETE_FILES
            while i and curline[i - 1] in FILENAME_CHARS:
                i -= 1
            comp_start = curline[i:j]
            j = i
            while i and curline[i - 1] in FILENAME_CHARS + os.sep:
                i -= 1
            comp_what = curline[i:j]
        elif hp.is_in_code() and (not mode or mode == COMPLETE_ATTRIBUTES):
            self._remove_autocomplete_window()
            mode = COMPLETE_ATTRIBUTES
            while i and curline[i - 1] in ID_CHARS:
                i -= 1
            comp_start = curline[i:j]
            if i and curline[i - 1] == '.':
                hp.set_index("insert-%dc" % (len(curline) - (i - 1)))
                comp_what = hp.get_expression()
                if not comp_what or \
                   (not evalfuncs and comp_what.find('(') != -1):
                    return
            else:
                comp_what = ""
        else:
            return

        if complete and not comp_what and not comp_start:
            return
        comp_lists = self.fetch_completions(comp_what, mode)
        if not comp_lists[0]:
            return
        self.autocompletewindow = self._make_autocomplete_window()
        self.autocompletewindow.show_window(comp_lists,
                                            "insert-%dc" % len(comp_start),
                                            complete, mode, userWantsWin)
        return True

    def fetch_completions(self, what, mode):
        """Return a pair of lists of completions for something. The first list
        is a sublist of the second. Both are sorted.

        If there is a Python subprocess, get the comp. list there.  Otherwise,
        either fetch_completions() is running in the subprocess itself or it
        was called in an IDLE EditorWindow before any script had been run.

        The subprocess environment is that of the most recently run script.  If
        two unrelated modules are being edited some calltips in the current
        module may be inoperative if the module was not the last to run.
        """
        try:
            rpcclt = self.editwin.flist.pyshell.interp.rpcclt
        except:
            rpcclt = None
        if rpcclt:
            return rpcclt.remotecall("exec", "get_the_completion_list",
                                     (what, mode), {})
        else:
            if mode == COMPLETE_ATTRIBUTES:
                if what == "":
                    namespace = __main__.__dict__.copy()
                    namespace.update(__main__.__builtins__.__dict__)
                    bigl = eval("dir()", namespace)
                    bigl.sort()
                    if "__all__" in bigl:
                        smalll = eval("__all__", namespace)
                        smalll.sort()
                    else:
                        smalll = filter(lambda s: s[:1] != '_', bigl)
                else:
                    try:
                        entity = self.get_entity(what)
                        bigl = dir(entity)
                        bigl.sort()
                        if "__all__" in bigl:
                            smalll = entity.__all__
                            smalll.sort()
                        else:
                            smalll = filter(lambda s: s[:1] != '_', bigl)
                    except:
                        return [], []

            elif mode == COMPLETE_FILES:
                if what == "":
                    what = "."
                try:
                    expandedpath = os.path.expanduser(what)
                    bigl = os.listdir(expandedpath)
                    bigl.sort()
                    smalll = filter(lambda s: s[:1] != '.', bigl)
                except OSError:
                    return [], []

            if not smalll:
                smalll = bigl
            return smalll, bigl

    def get_entity(self, name):
        """Lookup name in a namespace spanning sys.modules and __main.dict__"""
        namespace = sys.modules.copy()
        namespace.update(__main__.__dict__)
        return eval(name, namespace)
Beispiel #25
0
 elif args:
     enable_edit = True
     pathx = []
     for filename in args:
         pathx.append(os.path.dirname(filename))
     for dir in pathx:
         dir = os.path.abspath(dir)
         if not dir in sys.path:
             sys.path.insert(0, dir)
 else:
     dir = os.getcwd()
     if not dir in sys.path:
         sys.path.insert(0, dir)
 # check the IDLE settings configuration (but command line overrides)
 edit_start = idleConf.GetOption('main',
                                 'General',
                                 'editor-on-startup',
                                 type='bool')
 enable_edit = enable_edit or edit_start
 enable_shell = enable_shell or not edit_start
 # start editor and/or shell windows:
 root = Tk(className="Idle")
 fixwordbreaks(root)
 root.withdraw()
 flist = PyShellFileList(root)
 if enable_edit:
     if not (cmd or script):
         for filename in args:
             flist.open(filename)
         if not args:
             flist.new()
 if enable_shell:
Beispiel #26
0
 if self.fileencoding == BOM_UTF8 or failed:
     return BOM_UTF8 + chars.encode("utf-8")
 # Try the original file encoding next, if any
 if self.fileencoding:
     try:
         return chars.encode(self.fileencoding)
     except UnicodeError:
         tkMessageBox.showerror(
             "I/O Error",
             "Cannot save this as '%s' anymore. Saving as UTF-8" \
             % self.fileencoding,
             master = self.text)
         return BOM_UTF8 + chars.encode("utf-8")
 # Nothing was declared, and we had not determined an encoding
 # on loading. Recommend an encoding line.
 config_encoding = idleConf.GetOption("main", "EditorWindow",
                                      "encoding")
 if config_encoding == 'utf-8':
     # User has requested that we save files as UTF-8
     return BOM_UTF8 + chars.encode("utf-8")
 ask_user = True
 try:
     chars = chars.encode(encoding)
     enc = encoding
     if config_encoding == 'locale':
         ask_user = False
 except UnicodeError:
     chars = BOM_UTF8 + chars.encode("utf-8")
     enc = "utf-8"
 if not ask_user:
     return chars
 dialog = EncodingMessage(self.editwin.top, enc)
Beispiel #27
0
class AutoComplete:
    """
    Extension to allow automatic completion of text.

    Set the autocomplete and try-open-completions key bindings.
    Options:
        popupwait - how many millisecs to wait before opening window.

        onlycontaining - if True, the autocomplete window will first only show names that contain the typed text.
    If you double press the key for autocomplete, the list of names will grow to include all names.

        imports - if True, names available for import statements will be completed too.

        imports-timeout - (proximal) maximum delay until import completion returns.

        twotabstocomplete - sets if two tabs are required to complete text once window is open.

        entertocomplete - sets if pressing enter completes a name.

        dictkeys - if forced open is fired and cursor is just after '[', show window containing the dict keys.

        showlengths - allows showing of lengths of lists and tuples.
    """

    menudefs = [('edit', [
        ("Show Completions", "<<force-open-completions>>"),
    ])]

    popupwait = idleConf.GetOption("extensions",
                                   "AutoComplete",
                                   "popupwait",
                                   type="int",
                                   default=0)

    # Flag to show only completions that actually contain typed word.
    onlycontaining = idleConf.GetOption("extensions",
                                        "AutoComplete",
                                        "onlycontaining",
                                        type="bool",
                                        default=False,
                                        member_name='onlycontaining')

    # Flag to auto complete imports.
    imports = idleConf.GetOption("extensions",
                                 "AutoComplete",
                                 "imports",
                                 type="bool",
                                 default=False,
                                 member_name='imports')

    # Flag to complete after two tabs.
    twotabstocomplete = idleConf.GetOption("extensions",
                                           "AutoComplete",
                                           "twotabstocomplete",
                                           type="bool",
                                           default=True,
                                           member_name='twotabstocomplete')

    # Flag to complete when enter is pressed.
    entertocomplete = idleConf.GetOption("extensions",
                                         "AutoComplete",
                                         "entertocomplete",
                                         type="bool",
                                         default=False,
                                         member_name='entertocomplete')

    # Flag to show dictionary keys.
    dictkeys = idleConf.GetOption("extensions",
                                  "AutoComplete",
                                  "dictkeys",
                                  type="bool",
                                  default=False,
                                  member_name='dictkeys')

    # Flag to allow showing length of lists and tuples.
    showlengths = idleConf.GetOption("extensions",
                                     "AutoComplete",
                                     "showlengths",
                                     type="bool",
                                     default=False,
                                     member_name='showlengths')

    def __init__(self, editwin=None):
        self.editwin = editwin
        if editwin is None:  # subprocess and test
            return
        self.text = editwin.text
        self.autocompletewindow = None

        # id of delayed call, and the index of the text insert when the delayed
        # call was issued. If _delayed_completion_id is None, there is no
        # delayed call.
        self._delayed_completion_id = None
        self._delayed_completion_index = None

    def _make_autocomplete_window(self):
        return AutoCompleteWindow.AutoCompleteWindow(self.text,
                                                     self.twotabstocomplete,
                                                     self.entertocomplete)

    def _remove_autocomplete_window(self, event=None):
        if self.autocompletewindow:
            self.autocompletewindow.hide_window()
            self.autocompletewindow = None

    def is_executing(self):
        return isinstance(self.editwin, PyShell.PyShell) and \
            self.editwin.executing

    def force_open_completions_event(self, event):
        """
        Happens when the user really wants to open a completion list, even
        if a function call is needed.
        """
        if self.is_executing():
            return
        self.open_completions(True, False, True)

    def try_open_completions_event(self, event):
        """
        Happens when it would be nice to open a completion list, but not
        really necessary, for example after an dot, so function
        calls won't be made.
        """

        if self.is_executing():
            return

        lastchar = self.text.get("insert-1c")
        if lastchar == ".":
            self._open_completions_later(False, False, False,
                                         COMPLETE_ATTRIBUTES)
        elif lastchar in SEPS:
            self._open_completions_later(False, False, False, COMPLETE_FILES)

    def autocomplete_event(self, event):
        """
        Happens when the user wants to complete his word, and if necessary,
        open a completion list after that (if there is more than one
        completion)
        """
        if hasattr(event, "mc_state") and event.mc_state:
            # A modifier was pressed along with the tab, continue as usual.
            return
        if self.autocompletewindow and self.autocompletewindow.is_active():
            self.autocompletewindow.complete()
            return "break"
        else:
            opened = self.open_completions(False, True, True)
            if opened:
                return "break"

    def _open_completions_later(self, *args):
        self._delayed_completion_index = self.text.index("insert")
        if self._delayed_completion_id is not None:
            self.text.after_cancel(self._delayed_completion_id)
        self._delayed_completion_id = \
            self.text.after(self.popupwait, self._delayed_open_completions,
                            *args)

    def _delayed_open_completions(self, *args):
        self._delayed_completion_id = None
        if self.text.index("insert") != self._delayed_completion_index:
            return
        self.open_completions(*args)

    def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
        """Find the completions and create the AutoCompleteWindow.
        Return True if successful (no syntax error or so found).
        if complete is True, then if there's nothing to complete and no
        start of completion, won't open completions and return False.
        If mode is given, will open a completion list only in this mode.
        """
        # Cancel another delayed call, if it exists.
        if self._delayed_completion_id is not None:
            self.text.after_cancel(self._delayed_completion_id)
            self._delayed_completion_id = None

        # If the window is already open, show the big list of completions.
        # This means that a double Ctrl-space opens the big list every time, which is nice.
        if self.autocompletewindow is not None and self.autocompletewindow.autocompletewindow is not None:
            showbig = True
        else:
            showbig = False

        hp = HyperParser(self.editwin, "insert")
        curline = self.text.get("insert linestart", "insert")
        i = j = len(curline)

        # Check if it's an import statement.
        # This is seperated from the rest since the pattern check is in ModuleCompletion.
        imports = self.get_module_completion(curline)
        if imports:
            if imports == ([], []):
                return
            comp_lists = imports
            while i and curline[i - 1] in ID_CHARS:
                i -= 1
            comp_start = curline[i:j]
        elif self.dictkeys and hp.is_in_dict() and (
                not mode or mode == COMPLETE_KEYS) and evalfuncs:
            self._remove_autocomplete_window()
            mode = COMPLETE_KEYS
            while i and curline[i - 1] in ID_CHARS + '"' + "'":
                i -= 1
            comp_start = curline[i:j]
            if curline[i - 1:i] == "[":
                hp.set_index("insert-%dc" % (len(curline) - (i - 1)))
                comp_what = hp.get_expression()
            else:
                comp_what = ""
        elif (hp.is_in_string() or hp.is_in_command()) \
            and (not mode or mode==COMPLETE_FILES):
            self._remove_autocomplete_window()
            mode = COMPLETE_FILES
            while i and curline[i - 1] in FILENAME_CHARS:
                i -= 1
            comp_start = curline[i:j]
            j = i
            while i and curline[i - 1] in FILENAME_CHARS + SEPS:
                i -= 1
            comp_what = curline[i:j]
        elif hp.is_in_code() and (not mode or mode == COMPLETE_ATTRIBUTES):
            self._remove_autocomplete_window()
            mode = COMPLETE_ATTRIBUTES

            while i and curline[i - 1] in ID_CHARS:
                i -= 1
            comp_start = curline[i:j]
            if i and curline[i - 1] == '.':
                hp.set_index("insert-%dc" % (len(curline) - (i - 1)))
                comp_what = hp.get_expression()

                if not comp_what or \
                   (not evalfuncs and comp_what.find('(') != -1):
                    return
            else:
                comp_what = ""
        else:
            return

        # For everything but imports, call fetch_completions
        if not imports:
            if complete and not comp_what and not comp_start:
                return
            comp_lists = self.fetch_completions(comp_what, mode)
            if not comp_lists[0]:
                return

        # It's nice to be able to see the length of a tuple/list (but not anything more complicated)
        if comp_lists[0] == SHOWCALLTIP:
            parenleft = self.text.index('insert-1c')
            CallTip(self.text).showtip(comp_lists[1], parenleft,
                                       parenleft.split('.')[0] + '.end')
            return

        if mode == COMPLETE_ATTRIBUTES and not imports:
            calltips = self.editwin.extensions.get('CallTips')
            if calltips:
                args = calltips.arg_names(evalfuncs)
                if args:
                    args = [a + '=' for a in args]
                    comp_lists = sorted(comp_lists[0] +
                                        args), sorted(comp_lists[1] + args)

        # Check if we want to show only completion containing typed word.
        if self.onlycontaining:
            # Small optimization
            comp_lower = comp_start.lower()
            # Find such completions.
            comp_lists = [
                name for name in comp_lists[0] if comp_lower in name.lower()
            ], comp_lists[1]
            # If none were found, look in big list.
            if not comp_lists[0]:
                comp_lists = [
                    name for name in comp_lists[1]
                    if comp_lower in name.lower()
                ], comp_lists[1]
            # If still none were found, just return the big list - which is the default anyway.
            if not comp_lists[0]:
                comp_lists = comp_lists[1], comp_lists[1]

        if showbig:
            comp_lists = comp_lists[1], []

        self.autocompletewindow = self._make_autocomplete_window()
        return not self.autocompletewindow.show_window(
            comp_lists,
            "insert-%dc" % len(comp_start),
            complete,
            mode,
            userWantsWin,
            onlycontaining=self.onlycontaining)

    def fetch_completions(self, what, mode):
        """Return a pair of lists of completions for something. The first list
        is a sublist of the second. Both are sorted.

        If there is a Python subprocess, get the comp. list there.  Otherwise,
        either fetch_completions() is running in the subprocess itself or it
        was called in an IDLE EditorWindow before any script had been run.

        The subprocess environment is that of the most recently run script.  If
        two unrelated modules are being edited some calltips in the current
        module may be inoperative if the module was not the last to run.
        """
        try:
            rpcclt = self.editwin.flist.pyshell.interp.rpcclt
        except:
            rpcclt = None
        if rpcclt:
            return rpcclt.remotecall("exec", "get_the_completion_list",
                                     (what, mode), {})
        else:
            import run
            bigl = smalll = []
            if mode == COMPLETE_ATTRIBUTES:
                if what == "":
                    namespace = run.World.executive.locals.copy()
                    namespace.update(__main__.__builtins__.__dict__)
                    bigl = eval("dir()", namespace) + keyword.kwlist
                    bigl = sorted(set(bigl))
                    if "__all__" in bigl:
                        smalll = sorted(set(eval("__all__", namespace)))
                    else:
                        smalll = [s for s in bigl if s[:1] != '_']
                else:
                    try:
                        entity = self.get_entity(what)
                        bigl = dir(entity)
                        bigl = sorted(set(bigl))
                        if "__all__" in bigl:
                            smalll = sorted(set(entity.__all__))
                        else:
                            smalll = [s for s in bigl if s[:1] != '_']
                    except:
                        return [], []

            elif mode == COMPLETE_FILES:
                if what == "":
                    what = "."
                try:
                    from os.path import normcase
                    expandedpath = os.path.expanduser(what)
                    bigl = os.listdir(expandedpath)
                    try:
                        cmp_ = cmp
                    except NameError:
                        cmp_ = lambda x, y: (x > y) - (x < y)
                    bigl = sorted(
                        set(bigl),
                        cmp=lambda x, y: cmp_(normcase(x), normcase(y)))
                    smalll = [s for s in bigl if s[:1] != '.']
                except OSError:
                    return [], []
            elif mode == COMPLETE_KEYS:
                entity = None
                try:
                    entity = self.get_entity(what)
                    keys = set()
                    for key in entity.keys():
                        try:
                            r = repr(key)
                            if not r.startswith('<'):
                                keys.add(r)
                        except:
                            pass
                    smalll = bigl = sorted(keys)
                except:
                    # If the entity is a list or tuple let's show the length.
                    # It is so common to go back to the start of the line, write "len(", go to the end, write ")",
                    # evaluate, then remove the "len", and finally do what you actually wanted to do...
                    # In any case, it's configurable.
                    try:
                        if isinstance(entity, list):
                            return SHOWCALLTIP, 'list[0:%d]' % len(entity)
                        elif isinstance(entity, tuple):
                            return SHOWCALLTIP, 'tuple[0:%d]' % len(entity)
                        elif entity.__class__.__name__ == 'ndarray':
                            return SHOWCALLTIP, 'ndarray({}, dtype={})'.format(
                                entity.shape, entity.dtype)
                    except:
                        pass
                    return [], []

            if not smalll:
                smalll = bigl
            return smalll, bigl

    def get_entity(self, name):
        """Lookup name in a namespace spanning sys.modules and __main.dict__ or import module"""
        import run
        namespace = sys.modules.copy()
        namespace.update(run.World.executive.locals)
        return eval(name, namespace)

    @remoteboundmethod
    def get_module_completion(self, line):
        """Get module completions for the line"""
        return ModuleCompletion.module_completion(line)
Beispiel #28
0
"""About Dialog for IDLE"""
import os
import sys
from Tkinter import Toplevel, Frame, Button, Label, TkVersion
from Tkconstants import LEFT, NSEW, SUNKEN, EW, W, BOTH, TOP, BOTTOM

import idlever
import textView
from stylist import PoorManStyle
from configHandler import idleConf

TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
if TTK:
    from ttk import Frame, Button, Label, Style


class AboutDialog(Toplevel):
    """Modal about dialog for idle

    """
    def __init__(self, parent, title):
        Toplevel.__init__(self, parent)
        self.configure(borderwidth=5)

        self.geometry("+%d+%d" %
                      (parent.winfo_rootx() + 30, parent.winfo_rooty() + 30))
        self.bg = "#707070"
        self.fg = "#ffffff"

        self.SetupStyles()
        self.CreateWidgets()
Beispiel #29
0
class Squeezer:
    """
    Extension to squeeze long output into a button

    This file was originally copied for Tal Einat's Squeezer package.

    Options:
        max-num-of-lines - output bigger than this will be squeezed.

        min-num-of-lines - output smaller than this will *not* be squeezed.

        max-expand - output will never be more than this amount of characters.

        preview-command-nt(-win) - command to open preview application.

        squeeze-code - flag for allowing to squeeze code.

    Configure the key bindings expand-last-squeezed, preview-last-squeezed and squeeze-current-text.
    """
    _MAX_NUM_OF_LINES = idleConf.GetOption("extensions",
                                           "Squeezer",
                                           "max-num-of-lines",
                                           type="int",
                                           default=30)

    _MAX_EXPAND = idleConf.GetOption("extensions",
                                     "Squeezer",
                                     "max-expand",
                                     type="int",
                                     default=50000)

    _PREVIEW_COMMAND = idleConf.GetOption("extensions",
                                          "Squeezer",
                                          "preview-command-" +
                                          {"nt": "win"}.get(os.name, os.name),
                                          default="",
                                          raw=True)

    # Flag for whether or not stdin can be squeezing
    _SQUEEZE_CODE = idleConf.GetOption("extensions",
                                       "Squeezer",
                                       "squeeze-code",
                                       type="bool",
                                       default=False,
                                       member_name='_SQUEEZE_CODE')

    _MIN_NUM_OF_LINES = idleConf.GetOption("extensions",
                                           "Squeezer",
                                           "min-num-of-lines",
                                           type="int",
                                           default=1,
                                           member_name="_MIN_NUM_OF_LINES")

    menudefs = [(
        'edit',
        [
            None,  # Separator
            ("Expand last squeezed text", "<<expand-last-squeezed>>"),
        ])]
    if _PREVIEW_COMMAND:
        menudefs[0][1].append(
            ("Preview last squeezed text", "<<preview-last-squeezed>>"))

    def __init__(self, editwin):
        """
        Args:
            editwin (PyShell): pyshell window.
        """
        self.editwin = editwin
        self.text = text = editwin.text
        self.expandingbuttons = []
        if isinstance(editwin, PyShell):
            # If we get a PyShell instance, replace its write method with a
            # wrapper, which inserts an ExpandingButton instead of a long text.
            def mywrite(s, tags=(), write=editwin.write):
                if tags != "stdout":
                    return write(s, tags)
                else:
                    numoflines = self.count_lines(s)
                    if numoflines < self._MAX_NUM_OF_LINES:
                        return write(s, tags)
                    else:
                        expandingbutton = ExpandingButton(s,
                                                          tags,
                                                          numoflines,
                                                          self,
                                                          check_links=True)
                        text.mark_gravity("iomark", Tkinter.RIGHT)
                        text.window_create("iomark",
                                           window=expandingbutton,
                                           padx=3,
                                           pady=5)
                        text.see("iomark")
                        text.update()
                        text.mark_gravity("iomark", Tkinter.LEFT)
                        self.expandingbuttons.append(expandingbutton)

            editwin.write = mywrite

            # Add squeeze-current-text to the right-click menu
            text.bind("<<squeeze-current-text>>",
                      self.squeeze_current_text_event)
            _add_to_rmenu(
                editwin,
                [("_Squeeze current text", "<<squeeze-current-text>>")])

    def count_lines(self, s):
        """
        Calculate number of lines in given text.

        Before calculation, the tab width and line length of the text are
        fetched, so that up-to-date values are used.
        """
        # Tab width is configurable
        tabwidth = self.editwin.get_tabwidth()

        text = self.editwin.text
        # Get the Text widget's size
        linewidth = text.winfo_width()
        # Deduct the border and padding
        linewidth -= 2 * sum(
            [int(text.cget(opt)) for opt in ('border', 'padx')])

        # Get the Text widget's font
        font = tkFont.Font(text, name=text.cget('font'))

        # Divide the size of the Text widget by the font's width.
        # According to Tk8.4 docs, the Text widget's width is set
        # according to the width of its font's '0' (zero) character,
        # so we will use this as an approximation.
        linewidth //= font.measure('0')

        return _countlines(s, linewidth, tabwidth)

    def expand_last_squeezed_event(self, event):
        """Expands bottom most squeezed block"""
        if self.expandingbuttons:
            self.expandingbuttons[-1].expand(event)
        else:
            self.text.bell()
        return "break"

    def preview_last_squeezed_event(self, event):
        """Opens a defined application to show squeezed text"""
        if self._PREVIEW_COMMAND and self.expandingbuttons:
            self.expandingbuttons[-1].preview(event)
        else:
            self.text.bell()
        return "break"

    def squeeze_last_output_event(self, event):
        """Squeezes bottom most un-squeezed block"""
        last_console = self.text.tag_prevrange("console", Tkinter.END)
        if not last_console:
            return "break"

        prev_ranges = []

        if self._SQUEEZE_CODE:
            valid_tags = ("stdout", "stderr", "stdin")
        else:
            valid_tags = ("stdout", "stderr")

        for tag_name in valid_tags:
            rng = last_console
            while rng:
                rng = self.text.tag_prevrange(tag_name, rng[0])
                if rng:
                    txt = self.text.get(*rng).strip()
                    if txt and (
                            tag_name != 'stdin' or '\n' in txt
                    ) and self.count_lines(txt) > self._MIN_NUM_OF_LINES:
                        prev_ranges.append((rng, tag_name))
                        break
        if not prev_ranges:
            return "break"

        # Get max element dynamically, in case the number of allowed tags to squeeze ever changes.
        max_rng = prev_ranges[0]
        for rng in prev_ranges[1:]:
            if self.text.compare(max_rng[0][0], '<', rng[0][0]):
                max_rng = rng

        if not self.squeeze_range(*max_rng):
            self.text.bell()
        return "break"

    def squeeze_current_text_event(self, event):
        """Squeeze selected block"""
        insert_tag_names = self.text.tag_names(Tkinter.INSERT)
        for tag_name in ("stdout", "stderr"):
            if tag_name in insert_tag_names:
                break
        else:
            # Check if code squeezing is enabled.
            if self._SQUEEZE_CODE and 'stdin' in insert_tag_names:
                tag_name = 'stdin'
            else:
                # no tag associated with the index
                self.text.bell()
                return "break"

        # find the range to squeeze
        rng = self.text.tag_prevrange(tag_name, Tkinter.INSERT + "+1c")
        if not self.squeeze_range(rng, tag_name):
            self.text.bell()
        return "break"

    def squeeze_range(self, rng, tag_name):
        if not rng or rng[0] == rng[1]:
            return False
        start, end = rng

        # If it's code that we are squeezing then we only squeeze from the second row. I think this is nicer, because
        # mostly we'll be squeezing function definitions, and this will keep the 'def ...' visible.
        if tag_name == 'stdin':
            # It's nice to save the line just before, the "def" line, so it appears in preview and copy
            # of the ExpandingButton.
            def_line = self.text.get(start, start + " lineend")
            start = self.text.index("%s+1l linestart" % start)
        else:
            def_line = None

        old_expandingbutton = self.find_button(end)

        s = self.text.get(start, end)
        # if the last char is a newline, remove it from the range
        if s and s[-1] == '\n':
            end = self.text.index("%s-1c" % end)
            s = s[:-1]

        # This will preserve all tags, such as for colors and links
        rangetags = tags_in_range(self.text, start, end)
        # delete the text
        _get_base_text(self.editwin).delete(start, end)

        if old_expandingbutton is not None and \
           old_expandingbutton.tags == tag_name:
            old_expandingbutton.expand_back(s)
            return True

        # prepare an ExpandingButton
        numoflines = self.count_lines(s)
        expandingbutton = ExpandingButton(s,
                                          tag_name,
                                          numoflines,
                                          self,
                                          def_line=def_line,
                                          rangetags=rangetags)
        # insert the ExpandingButton to the Text
        self.text.window_create(start, window=expandingbutton, padx=3, pady=5)
        # insert the ExpandingButton to the list of ExpandingButtons
        i = len(self.expandingbuttons)
        while i > 0 and self.text.compare(self.expandingbuttons[i - 1], ">",
                                          expandingbutton):
            i -= 1
        self.expandingbuttons.insert(i, expandingbutton)
        return True

    def find_button(self, pos):
        for btn in self.expandingbuttons:
            try:
                if self.text.compare(pos, "==", btn):
                    return btn
            except Tkinter.TclError:
                pass
        return None
"""
An auto-completion window for IDLE, used by the AutoComplete extension
"""
from Tkinter import Toplevel, Scrollbar, Listbox
from Tkinter import TclError
from Tkinter import END, VERTICAL, RIGHT, LEFT, BOTH, Y
from idlesporklib.MultiCall import MC_SHIFT
from idlesporklib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES

from configHandler import idleConf
theme = idleConf.GetOption('main', 'Theme', 'name')

HIDE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-hide>>"
HIDE_SEQUENCES = ("<FocusOut>", "<ButtonPress>")
KEYPRESS_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keypress>>"
# We need to bind event beyond <Key> so that the function will be called
# before the default specific IDLE function
KEYPRESS_SEQUENCES = ("<Key>", "<Key-BackSpace>", "<Key-Return>", "<Key-Tab>",
                      "<Key-Up>", "<Key-Down>", "<Key-Home>", "<Key-End>",
                      "<Key-Prior>", "<Key-Next>")
KEYRELEASE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keyrelease>>"
KEYRELEASE_SEQUENCE = "<KeyRelease>"
LISTUPDATE_SEQUENCE = "<B1-ButtonRelease>"
WINCONFIG_SEQUENCE = "<Configure>"
DOUBLECLICK_SEQUENCE = "<B1-Double-ButtonRelease>"


class AutoCompleteWindow:
    # Save global instance to make sure there is always at most one window.
    instance = None