예제 #1
0
 def print_window(self, event):
     confirm = tkMessageBox.askokcancel(title="Print",
                                        message="Print to Default Printer",
                                        default=tkMessageBox.OK,
                                        parent=self.text)
     if not confirm:
         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 = True
     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 = False
     if printPlatform:  #we can try to print for this platform
         command = command % pipes.quote(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,
                                    parent=self.text)
     else:  #no printing for this platform
         message = "Printing is not enabled for this platform: %s" % platform
         tkMessageBox.showinfo("Print status", message, parent=self.text)
     if tempfilename:
         os.unlink(tempfilename)
     return "break"
예제 #2
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
        if not self.editwin.get_saved():
            autosave = idleConf.GetOption('main',
                                          'General',
                                          'autosave',
                                          type='bool')
            if autosave and filename:
                self.editwin.io.save(None)
            else:
                confirm = self.ask_save_dialog()
                self.editwin.text.focus_set()
                if confirm:
                    self.editwin.io.save(None)
                    filename = self.editwin.io.filename
                else:
                    filename = None
        return filename
예제 #3
0
 def test_init(self):
     self.assertIs(self.history.text, self.text)
     self.assertEqual(self.history.history, [])
     self.assertIsNone(self.history.prefix)
     self.assertIsNone(self.history.pointer)
     self.assertEqual(self.history.cyclic,
             idleConf.GetOption("main", "History",  "cyclic", 1, "bool"))
예제 #4
0
    def __init__(self, pyshell):
        """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).

        @type pyshell: idlesporklib.PyShell.PyShell
        """
        if 'SPORKPATH' not in os.environ.keys():
            self.ph = Prehistory(os.path.expanduser('~'))
        else:
            self.ph = Prehistory(os.environ['SPORKPATH'])
        self.pyshell = pyshell
        self.text = text = pyshell.text
        self.history = self.ph.get()
        self.super_history = list(enumerate(self.history))
        self.smart_history = []
        self.prefix = None
        self.pointer = None
        self.suggested = []
        self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1,
                                         "bool")
        text.bind("<<history-previous>>", self.history_prev)
        text.bind("<<history-next>>", self.history_next)
        text.bind("<<history-guess>>", self.history_guess)
예제 #5
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) as f:
            contents = f.read().decode(encoding='utf-8')
        self.parser.feed(contents)
        self['state'] = 'disabled'
예제 #6
0
    class __metaclass__(EnablableExtension.__metaclass__):
        _index_by_previous_line = idleConf.GetOption("extensions", "OutHist",

                                                     "index_by_previous_line", type="bool", default=False,
                                                     member_name='index_by_previous_line')
        just_changed = False

        @property
        def index_by_previous_line(cls):
            return cls._index_by_previous_line

        @index_by_previous_line.setter
        def index_by_previous_line(cls, value):
            cls._index_by_previous_line = value
            cls.set_index_by_previous_line(value)
예제 #7
0
    def format_paragraph_event(self, event, limit=None):
        """Formats paragraph to a max width specified in idleConf.

        If text is selected, format_paragraph_event will start breaking lines
        at the max width, starting from the beginning selection.

        If no text is selected, format_paragraph_event uses the current
        cursor location to determine the paragraph (lines of text surrounded
        by blank lines) and formats it.

        The length limit parameter is for testing with a known value.
        """
        if limit is None:
            # The default length limit is that defined by pep8
            limit = idleConf.GetOption(
                'extensions', 'FormatParagraph', 'max-width',
                type='int', default=72)
        text = self.editwin.text
        first, last = self.editwin.get_selection_indices()
        if first and last:
            data = text.get(first, last)
            comment_header = get_comment_header(data)
        else:
            first, last, comment_header, data = \
                    find_paragraph(text, text.index("insert"))
        if comment_header:
            newdata = reformat_comment(data, limit, comment_header)
        else:
            newdata = reformat_paragraph(data, limit)
        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"
예제 #8
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)
예제 #9
0
class ModuleCompletion(object):
    """

    """
    # Time in seconds after which to return
    TIMEOUT_GIVEUP = idleConf.GetOption("extensions",
                                        "AutoComplete",
                                        "imports-timeout",
                                        type="int",
                                        default=2)

    # Regular expression for the python import statement
    import_re = re.compile(r'(?P<name>[a-zA-Z_][a-zA-Z0-9_]*?)'
                           r'(?P<package>[/\\]__init__)?'
                           r'(?P<suffix>%s)$' %
                           r'|'.join(re.escape(s) for s in _suffixes))

    rootmodules_cache = {}

    allobjs = None
    executing_patch = False
    patched = False

    @staticmethod
    def module_list(path):
        """
        Return the list containing the names of the modules available in the given
        folder.
        """
        # sys.path has the cwd as an empty string, but isdir/listdir need it as '.'
        if path == '':
            path = '.'

        # A few local constants to be used in loops below
        pjoin = os.path.join

        if os.path.isdir(path):
            # Build a list of all files in the directory and all files
            # in its subdirectories. For performance reasons, do not
            # recurse more than one level into subdirectories.
            files = []
            for root, dirs, nondirs in os.walk(path, followlinks=True):
                subdir = root[len(path) + 1:]
                if subdir:
                    files.extend(pjoin(subdir, f) for f in nondirs)
                    dirs[:] = [
                    ]  # Do not recurse into additional subdirectories.
                else:
                    files.extend(nondirs)

        else:
            try:
                files = list(zipimporter(path)._files.keys())
            except:
                files = []

        # Build a list of modules which match the import_re regex.
        modules = []
        for f in files:
            m = ModuleCompletion.import_re.match(f)
            if m:
                modules.append(m.group('name'))
        return list(set(modules))

    @staticmethod
    def get_root_modules():
        """
        Returns a list containing the names of all the modules available in the
        folders of the pythonpath.
        """
        rootmodules_cache = ModuleCompletion.rootmodules_cache
        rootmodules = list(sys.builtin_module_names)
        start_time = time()
        store = False
        for path in sys.path:
            try:
                modules = rootmodules_cache[path]
            except KeyError:
                modules = ModuleCompletion.module_list(path)
                try:
                    modules.remove('__init__')
                except ValueError:
                    pass
                if path not in ('', '.'):  # cwd modules should not be cached
                    rootmodules_cache[path] = modules
            rootmodules.extend(modules)
            if time() - start_time > ModuleCompletion.TIMEOUT_GIVEUP:
                store = True
                break
        if store:
            ModuleCompletion.rootmodules_cache = rootmodules_cache
        rootmodules = list(set(rootmodules))
        return rootmodules

    @staticmethod
    def is_importable(module, attr, only_modules):
        if only_modules:
            return inspect.ismodule(getattr(module, attr))
        else:
            return not (attr[:2] == '__' and attr[-2:] == '__')

    @staticmethod
    def try_import(mod, only_modules=False):
        mod = mod.rstrip('.')
        try:
            m = __import__(mod)
        except:
            return []
        mods = mod.split('.')
        for module in mods[1:]:
            m = getattr(m, module)

        m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__

        completions = []
        if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
            completions.extend([
                attr for attr in dir(m)
                if ModuleCompletion.is_importable(m, attr, only_modules)
            ])

        completions.extend(getattr(m, '__all__', []))
        if m_is_init:
            completions.extend(
                ModuleCompletion.module_list(os.path.dirname(m.__file__)))
        completions = {c for c in completions if isinstance(c, (str, unicode))}
        completions.discard('__init__')
        return list(completions)

    @staticmethod
    def module_completion(line):
        """
        Returns a list containing the completion possibilities for an import line.

        The line looks like this :
        'import xml.d'
        'from xml.dom import'
        """

        words = line.split(' ')
        nwords = len(words)
        mods = []
        lastword = ''

        # from whatever <tab> -> 'import '
        if nwords >= 3 and words[-3] == 'from':
            return None
        # 'from xyz import abc<tab>'
        elif nwords >= 4 and words[-4] == 'from':
            mod = words[-3]
            mods = ModuleCompletion.try_import(mod)
            lastword = words[-1]
        # 'from xy<tab>' or 'import xy<tab>'
        elif nwords >= 2 and words[-2] in {'import', 'from'}:
            if words[-1] == '':
                mods = ModuleCompletion.get_root_modules()
            else:
                mod = words[-1].split('.')
                if len(mod) < 2:
                    mods = ModuleCompletion.get_root_modules()
                else:
                    mods = ModuleCompletion.try_import('.'.join(mod[:-1]),
                                                       True)
                    lastword = mod[-1]

        if mods:
            if lastword:
                withcase = [mod for mod in mods if mod.startswith(lastword)]
                if withcase:
                    mods = withcase
                # if none, then case insensitive ones are ok too
                else:
                    text_low = lastword.lower()
                    mods = [
                        mod for mod in mods if mod.lower().startswith(text_low)
                    ]
            mods = sorted(mods)
            if lastword.startswith('_'):
                return mods, mods
            else:
                return [mod for mod in mods if not mod.startswith('_')], mods
예제 #10
0
class CustomizePrompt(object):
    """
    Extension to customize prompt line. Whatever you enter is given to `time.strftime`,
    with the following three new directives:
        %dM - minutes since last execution

        %dS - seconds since last execution

        %df - deci-seconds since last execution

        %OutIndex - output number. It relates to previous output or next depending on the `index_by_previous_line`
            option in the OutHist extension.
    """

    _PROMPT_FORMAT = idleConf.GetOption("extensions",
                                        "CustomizePrompt",
                                        "prompt-format",
                                        type="str",
                                        default='>>> ',
                                        member_name="_PROMPT_FORMAT")

    last_prompt = None

    def __init__(self, editwin):
        def showprompt(self_):
            if CustomizePrompt.last_prompt:
                time_diff = time.time() - CustomizePrompt.last_prompt
                mins, secs = divmod(time_diff, 60)
                mins, wsecs = int(mins), int(secs)
                ms = int((secs - wsecs) * 100)
            else:
                mins, wsecs, ms = 0, 0, 0

            s = CustomizePrompt._PROMPT_FORMAT.strip()
            s = s.replace('%dM', '%02d' % mins)
            s = s.replace('%dS', '%02d' % wsecs)
            s = s.replace('%df', '%02d' % ms)

            if '%OutIndex' in s and OutHist.enable:
                s = s.replace('%OutIndex', str(OutHist.cursor))

            self_.resetoutput()
            s = time.strftime(s + ' ')
            sys.ps1 = s
            self_.console.write(s)
            self_.text.mark_set("insert", "end-1c")
            self_.set_line_and_column()
            self_.io.reset_undo()

        try:
            old_runcmd_from_source = editwin.interp.runcmd_from_source
        # In case it's a file editor window.
        except AttributeError:
            return

        def runcmd_from_source(self_, line):
            CustomizePrompt.last_prompt = time.time()
            return old_runcmd_from_source(line)

        editwin.showprompt = MethodType(showprompt, editwin)
        editwin.interp.runcmd_from_source = MethodType(runcmd_from_source,
                                                       editwin.interp)
예제 #11
0
class ParenMatch:
    """Highlight matching parentheses

    There are three supported style of paren matching, based loosely
    on the Emacs options.  The style is select based on the
    HILITE_STYLE attribute; it can be changed used the set_style
    method.

    The supported styles are:

    default -- When a right paren is typed, highlight the matching
        left paren for 1/2 sec.

    expression -- When a right paren is typed, highlight the entire
        expression from the left paren to the right paren.

    TODO:
        - extend IDLE with configuration dialog to change options
        - implement rest of Emacs highlight styles (see below)
        - print mismatch warning in IDLE status window

    Note: In Emacs, there are several styles of highlight where the
    matching paren is highlighted whenever the cursor is immediately
    to the right of a right paren.  I don't know how to do that in Tk,
    so I haven't bothered.
    """
    menudefs = [
        ('edit', [
            ("Show surrounding parens", "<<flash-paren>>"),
        ])
    ]
    STYLE = idleConf.GetOption('extensions','ParenMatch','style',
            default='expression')
    FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay',
            type='int',default=500)
    HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite')
    BELL = idleConf.GetOption('extensions','ParenMatch','bell',
            type='bool',default=1)

    RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>"
    # We want the restore event be called before the usual return and
    # backspace events.
    RESTORE_SEQUENCES = ("<KeyPress>", "<ButtonPress>",
                         "<Key-Return>", "<Key-BackSpace>")

    def __init__(self, editwin):
        self.editwin = editwin
        self.text = editwin.text
        # Bind the check-restore event to the function restore_event,
        # so that we can then use activate_restore (which calls event_add)
        # and deactivate_restore (which calls event_delete).
        editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME,
                          self.restore_event)
        self.counter = 0
        self.is_restore_active = 0
        self.set_style(self.STYLE)

    def activate_restore(self):
        if not self.is_restore_active:
            for seq in self.RESTORE_SEQUENCES:
                self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
            self.is_restore_active = True

    def deactivate_restore(self):
        if self.is_restore_active:
            for seq in self.RESTORE_SEQUENCES:
                self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
            self.is_restore_active = False

    def set_style(self, style):
        self.STYLE = style
        if style == "default":
            self.create_tag = self.create_tag_default
            self.set_timeout = self.set_timeout_last
        elif style == "expression":
            self.create_tag = self.create_tag_expression
            self.set_timeout = self.set_timeout_none

    def flash_paren_event(self, event):
        indices = (HyperParser(self.editwin, "insert")
                   .get_surrounding_brackets())
        if indices is None:
            self.warn_mismatched()
            return
        self.activate_restore()
        self.create_tag(indices)
        self.set_timeout_last()

    def paren_closed_event(self, event):
        # If it was a shortcut and not really a closing paren, quit.
        closer = self.text.get("insert-1c")
        if closer not in _openers:
            return
        hp = HyperParser(self.editwin, "insert-1c")
        if not hp.is_in_code():
            return
        indices = hp.get_surrounding_brackets(_openers[closer], True)
        if indices is None:
            self.warn_mismatched()
            return
        self.activate_restore()
        self.create_tag(indices)
        self.set_timeout()

    def restore_event(self, event=None):
        self.text.tag_delete("paren")
        self.deactivate_restore()
        self.counter += 1   # disable the last timer, if there is one.

    def handle_restore_timer(self, timer_count):
        if timer_count == self.counter:
            self.restore_event()

    def warn_mismatched(self):
        if self.BELL:
            self.text.bell()

    # any one of the create_tag_XXX methods can be used depending on
    # the style

    def create_tag_default(self, indices):
        """Highlight the single paren that matches"""
        self.text.tag_add("paren", indices[0])
        self.text.tag_config("paren", self.HILITE_CONFIG)

    def create_tag_expression(self, indices):
        """Highlight the entire expression"""
        if self.text.get(indices[1]) in (')', ']', '}'):
            rightindex = indices[1]+"+1c"
        else:
            rightindex = indices[1]
        self.text.tag_add("paren", indices[0], rightindex)
        self.text.tag_config("paren", self.HILITE_CONFIG)

    # any one of the set_timeout_XXX methods can be used depending on
    # the style

    def set_timeout_none(self):
        """Highlight will remain until user input turns it off
        or the insert has moved"""
        # After CHECK_DELAY, call a function which disables the "paren" tag
        # if the event is for the most recent timer and the insert has changed,
        # or schedules another call for itself.
        self.counter += 1
        def callme(callme, self=self, c=self.counter,
                   index=self.text.index("insert")):
            if index != self.text.index("insert"):
                self.handle_restore_timer(c)
            else:
                self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
        self.editwin.text_frame.after(CHECK_DELAY, callme, callme)

    def set_timeout_last(self):
        """The last highlight created will be removed after .5 sec"""
        # associate a counter with an event; only disable the "paren"
        # tag if the event is for the most recent timer.
        self.counter += 1
        self.editwin.text_frame.after(
            self.FLASH_DELAY,
            lambda self=self, c=self.counter: self.handle_restore_timer(c))
예제 #12
0
class CodeContext:
    menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
    context_depth = idleConf.GetOption("extensions",
                                       "CodeContext",
                                       "numlines",
                                       type="int",
                                       default=3)
    bgcolor = idleConf.GetOption("extensions",
                                 "CodeContext",
                                 "bgcolor",
                                 type="str",
                                 default="LightGray")
    fgcolor = idleConf.GetOption("extensions",
                                 "CodeContext",
                                 "fgcolor",
                                 type="str",
                                 default="Black")

    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)

    def toggle_code_context_event(self, event=None):
        if not self.label:
            # Calculate the border width and horizontal padding required to
            # align the context with the text in the main Text widget.
            #
            # All values are passed through int(str(<value>)), since some
            # values may be pixel objects, which can't simply be added to ints.
            widgets = self.editwin.text, self.editwin.text_frame
            # Calculate the required vertical padding
            padx = 0
            for widget in widgets:
                padx += int(str(widget.pack_info()['padx']))
                padx += int(str(widget.cget('padx')))
            # Calculate the required border width
            border = 0
            for widget in widgets:
                border += int(str(widget.cget('border')))
            self.label = Tkinter.Label(
                self.editwin.top,
                text="\n" * (self.context_depth - 1),
                anchor=W,
                justify=LEFT,
                font=self.textfont,
                bg=self.bgcolor,
                fg=self.fgcolor,
                width=1,  #don't request more than we get
                padx=padx,
                border=border,
                relief=SUNKEN)
            # Pack the label widget before and above the text_frame widget,
            # thus ensuring that it will appear directly above text_frame
            self.label.pack(side=TOP,
                            fill=X,
                            expand=False,
                            before=self.editwin.text_frame)
        else:
            self.label.destroy()
            self.label = None
        idleConf.SetOption("extensions", "CodeContext", "visible",
                           str(self.label is not None))
        idleConf.SaveUserCfgFiles()

    def get_line_info(self, linenum):
        """Get the line indent value, text, and any block start keyword

        If the line does not start a block, the keyword value is False.
        The indentation of empty lines (or comment lines) is INFINITY.

        """
        text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
        spaces, firstword = getspacesfirstword(text)
        opener = firstword in BLOCKOPENERS and firstword
        if len(text) == len(spaces) or text[len(spaces)] == '#':
            indent = INFINITY
        else:
            indent = len(spaces)
        return indent, text, opener

    def get_context(self, new_topvisible, stopline=1, stopindent=0):
        """Get context lines, starting at new_topvisible and working backwards.

        Stop when stopline or stopindent is reached. Return a tuple of context
        data and the indent level at the top of the region inspected.

        """
        assert stopline > 0
        lines = []
        # The indentation level we are currently in:
        lastindent = INFINITY
        # For a line to be interesting, it must begin with a block opening
        # keyword, and have less indentation than lastindent.
        for linenum in xrange(new_topvisible, stopline - 1, -1):
            indent, text, opener = self.get_line_info(linenum)
            if indent < lastindent:
                lastindent = indent
                if opener in ("else", "elif"):
                    # We also show the if statement
                    lastindent += 1
                if opener and linenum < new_topvisible and indent >= stopindent:
                    lines.append((linenum, indent, text, opener))
                if lastindent <= stopindent:
                    break
        lines.reverse()
        return lines, lastindent

    def update_code_context(self):
        """Update context information and lines visible in the context pane.

        """
        new_topvisible = int(self.text.index("@0,0").split('.')[0])
        if self.topvisible == new_topvisible:  # haven't scrolled
            return
        if self.topvisible < new_topvisible:  # scroll down
            lines, lastindent = self.get_context(new_topvisible,
                                                 self.topvisible)
            # retain only context info applicable to the region
            # between topvisible and new_topvisible:
            while self.info[-1][1] >= lastindent:
                del self.info[-1]
        elif self.topvisible > new_topvisible:  # scroll up
            stopindent = self.info[-1][1] + 1
            # retain only context info associated
            # with lines above new_topvisible:
            while self.info[-1][0] >= new_topvisible:
                stopindent = self.info[-1][1]
                del self.info[-1]
            lines, lastindent = self.get_context(new_topvisible,
                                                 self.info[-1][0] + 1,
                                                 stopindent)
        self.info.extend(lines)
        self.topvisible = new_topvisible
        # empty lines in context pane:
        context_strings = [""] * max(0, self.context_depth - len(self.info))
        # followed by the context hint lines:
        context_strings += [x[2] for x in self.info[-self.context_depth:]]
        self.label["text"] = '\n'.join(context_strings)

    def timer_event(self):
        if self.label:
            self.update_code_context()
        self.text.after(UPDATEINTERVAL, self.timer_event)

    def font_timer_event(self):
        newtextfont = self.text["font"]
        if self.label and newtextfont != self.textfont:
            self.textfont = newtextfont
            self.label["font"] = self.textfont
        self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
예제 #13
0
 def encode(self, chars):
     if isinstance(chars, str):
         # This is either plain ASCII, or Tk was returning mixed-encoding
         # text to us. Don't try to guess further.
         return chars
     # See whether there is anything non-ASCII in it.
     # If not, no need to figure out the encoding.
     try:
         return chars.encode('ascii')
     except UnicodeError:
         pass
     # If there is an encoding declared, try this first.
     try:
         enc = coding_spec(chars)
         failed = None
     except LookupError as msg:
         failed = msg
         enc = None
     if enc:
         try:
             return chars.encode(enc)
         except UnicodeError:
             failed = "Invalid encoding '%s'" % enc
     if failed:
         tkMessageBox.showerror("I/O Error",
                                "%s. Saving as UTF-8" % failed,
                                parent=self.text)
     # If there was a UTF-8 signature, use that. This should not fail
     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,
                 parent = 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)
     dialog.go()
     if dialog.num == 1:
         # User asked us to edit the file
         encline = "# -*- coding: %s -*-\n" % enc
         firstline = self.text.get("1.0", "2.0")
         if firstline.startswith("#!"):
             # Insert encoding after #! line
             self.text.insert("2.0", encline)
         else:
             self.text.insert("1.0", encline)
         return self.encode(self.text.get("1.0", "end-1c"))
     return chars