From 7aced17437a6b05bc4b0b5ff93aa6a5d3a374d68 Mon Sep 17 00:00:00 2001 From: David Scherer Date: Tue, 15 Aug 2000 01:13:23 +0000 Subject: Initial revision --- Lib/idlelib/AutoExpand.py | 92 ++++ Lib/idlelib/AutoIndent.py | 554 ++++++++++++++++++++ Lib/idlelib/Bindings.py | 67 +++ Lib/idlelib/CallTipWindow.py | 71 +++ Lib/idlelib/CallTips.py | 190 +++++++ Lib/idlelib/ChangeLog | 1017 +++++++++++++++++++++++++++++++++++++ Lib/idlelib/ClassBrowser.py | 224 ++++++++ Lib/idlelib/ColorDelegator.py | 234 +++++++++ Lib/idlelib/ConfigParser.py | 382 ++++++++++++++ Lib/idlelib/Debugger.py | 308 +++++++++++ Lib/idlelib/Delegator.py | 34 ++ Lib/idlelib/EditorWindow.py | 749 +++++++++++++++++++++++++++ Lib/idlelib/ExecBinding.py | 198 ++++++++ Lib/idlelib/FileList.py | 150 ++++++ Lib/idlelib/FormatParagraph.py | 155 ++++++ Lib/idlelib/FrameViewer.py | 38 ++ Lib/idlelib/GrepDialog.py | 135 +++++ Lib/idlelib/IOBinding.py | 254 +++++++++ Lib/idlelib/Icons/folder.gif | Bin 0 -> 120 bytes Lib/idlelib/Icons/minusnode.gif | Bin 0 -> 75 bytes Lib/idlelib/Icons/openfolder.gif | Bin 0 -> 125 bytes Lib/idlelib/Icons/plusnode.gif | Bin 0 -> 79 bytes Lib/idlelib/Icons/python.gif | Bin 0 -> 895 bytes Lib/idlelib/Icons/tk.gif | Bin 0 -> 85 bytes Lib/idlelib/IdleConf.py | 113 +++++ Lib/idlelib/IdleHistory.py | 89 ++++ Lib/idlelib/MultiScrolledLists.py | 138 +++++ Lib/idlelib/MultiStatusBar.py | 32 ++ Lib/idlelib/NEWS.txt | 130 +++++ Lib/idlelib/ObjectBrowser.py | 151 ++++++ Lib/idlelib/OldStackViewer.py | 276 ++++++++++ Lib/idlelib/OutputWindow.py | 279 ++++++++++ Lib/idlelib/ParenMatch.py | 192 +++++++ Lib/idlelib/PathBrowser.py | 95 ++++ Lib/idlelib/Percolator.py | 85 ++++ Lib/idlelib/PyParse.py | 569 +++++++++++++++++++++ Lib/idlelib/PyShell.py | 860 +++++++++++++++++++++++++++++++ Lib/idlelib/README.txt | 121 +++++ Lib/idlelib/Remote.py | 101 ++++ Lib/idlelib/ReplaceDialog.py | 172 +++++++ Lib/idlelib/ScriptBinding.py | 169 ++++++ Lib/idlelib/ScrolledList.py | 139 +++++ Lib/idlelib/SearchBinding.py | 97 ++++ Lib/idlelib/SearchDialog.py | 67 +++ Lib/idlelib/SearchDialogBase.py | 129 +++++ Lib/idlelib/SearchEngine.py | 221 ++++++++ Lib/idlelib/Separator.py | 92 ++++ Lib/idlelib/StackViewer.py | 135 +++++ Lib/idlelib/TODO.txt | 205 ++++++++ Lib/idlelib/ToolTip.py | 87 ++++ Lib/idlelib/TreeWidget.py | 471 +++++++++++++++++ Lib/idlelib/UndoDelegator.py | 352 +++++++++++++ Lib/idlelib/WidgetRedirector.py | 92 ++++ Lib/idlelib/WindowList.py | 85 ++++ Lib/idlelib/ZoomHeight.py | 46 ++ Lib/idlelib/__init__.py | 1 + Lib/idlelib/config-unix.txt | 3 + Lib/idlelib/config-win.txt | 3 + Lib/idlelib/config.txt | 66 +++ Lib/idlelib/eventparse.py | 93 ++++ Lib/idlelib/extend.txt | 106 ++++ Lib/idlelib/help.txt | 155 ++++++ Lib/idlelib/idle.bat | 3 + Lib/idlelib/idle.py | 12 + Lib/idlelib/idle.pyw | 12 + Lib/idlelib/idlever.py | 1 + Lib/idlelib/keydefs.py | 55 ++ Lib/idlelib/loader.py | 64 +++ Lib/idlelib/protocol.py | 369 ++++++++++++++ Lib/idlelib/pyclbr.py | 336 ++++++++++++ Lib/idlelib/spawn.py | 59 +++ Lib/idlelib/tabnanny.py | 372 ++++++++++++++ Lib/idlelib/testcode.py | 31 ++ 73 files changed, 12383 insertions(+) create mode 100644 Lib/idlelib/AutoExpand.py create mode 100644 Lib/idlelib/AutoIndent.py create mode 100644 Lib/idlelib/Bindings.py create mode 100644 Lib/idlelib/CallTipWindow.py create mode 100644 Lib/idlelib/CallTips.py create mode 100644 Lib/idlelib/ChangeLog create mode 100644 Lib/idlelib/ClassBrowser.py create mode 100644 Lib/idlelib/ColorDelegator.py create mode 100644 Lib/idlelib/ConfigParser.py create mode 100644 Lib/idlelib/Debugger.py create mode 100644 Lib/idlelib/Delegator.py create mode 100644 Lib/idlelib/EditorWindow.py create mode 100644 Lib/idlelib/ExecBinding.py create mode 100644 Lib/idlelib/FileList.py create mode 100644 Lib/idlelib/FormatParagraph.py create mode 100644 Lib/idlelib/FrameViewer.py create mode 100644 Lib/idlelib/GrepDialog.py create mode 100644 Lib/idlelib/IOBinding.py create mode 100644 Lib/idlelib/Icons/folder.gif create mode 100644 Lib/idlelib/Icons/minusnode.gif create mode 100644 Lib/idlelib/Icons/openfolder.gif create mode 100644 Lib/idlelib/Icons/plusnode.gif create mode 100644 Lib/idlelib/Icons/python.gif create mode 100644 Lib/idlelib/Icons/tk.gif create mode 100644 Lib/idlelib/IdleConf.py create mode 100644 Lib/idlelib/IdleHistory.py create mode 100644 Lib/idlelib/MultiScrolledLists.py create mode 100644 Lib/idlelib/MultiStatusBar.py create mode 100644 Lib/idlelib/NEWS.txt create mode 100644 Lib/idlelib/ObjectBrowser.py create mode 100644 Lib/idlelib/OldStackViewer.py create mode 100644 Lib/idlelib/OutputWindow.py create mode 100644 Lib/idlelib/ParenMatch.py create mode 100644 Lib/idlelib/PathBrowser.py create mode 100644 Lib/idlelib/Percolator.py create mode 100644 Lib/idlelib/PyParse.py create mode 100644 Lib/idlelib/PyShell.py create mode 100644 Lib/idlelib/README.txt create mode 100644 Lib/idlelib/Remote.py create mode 100644 Lib/idlelib/ReplaceDialog.py create mode 100644 Lib/idlelib/ScriptBinding.py create mode 100644 Lib/idlelib/ScrolledList.py create mode 100644 Lib/idlelib/SearchBinding.py create mode 100644 Lib/idlelib/SearchDialog.py create mode 100644 Lib/idlelib/SearchDialogBase.py create mode 100644 Lib/idlelib/SearchEngine.py create mode 100644 Lib/idlelib/Separator.py create mode 100644 Lib/idlelib/StackViewer.py create mode 100644 Lib/idlelib/TODO.txt create mode 100644 Lib/idlelib/ToolTip.py create mode 100644 Lib/idlelib/TreeWidget.py create mode 100644 Lib/idlelib/UndoDelegator.py create mode 100644 Lib/idlelib/WidgetRedirector.py create mode 100644 Lib/idlelib/WindowList.py create mode 100644 Lib/idlelib/ZoomHeight.py create mode 100644 Lib/idlelib/__init__.py create mode 100644 Lib/idlelib/config-unix.txt create mode 100644 Lib/idlelib/config-win.txt create mode 100644 Lib/idlelib/config.txt create mode 100644 Lib/idlelib/eventparse.py create mode 100644 Lib/idlelib/extend.txt create mode 100644 Lib/idlelib/help.txt create mode 100755 Lib/idlelib/idle.bat create mode 100644 Lib/idlelib/idle.py create mode 100644 Lib/idlelib/idle.pyw create mode 100644 Lib/idlelib/idlever.py create mode 100644 Lib/idlelib/keydefs.py create mode 100644 Lib/idlelib/loader.py create mode 100644 Lib/idlelib/protocol.py create mode 100644 Lib/idlelib/pyclbr.py create mode 100644 Lib/idlelib/spawn.py create mode 100644 Lib/idlelib/tabnanny.py create mode 100644 Lib/idlelib/testcode.py diff --git a/Lib/idlelib/AutoExpand.py b/Lib/idlelib/AutoExpand.py new file mode 100644 index 0000000..0d57be4 --- /dev/null +++ b/Lib/idlelib/AutoExpand.py @@ -0,0 +1,92 @@ +import string +import re + +###$ event <> +###$ win +###$ unix + +class AutoExpand: + + keydefs = { + '<>': [''], + } + + unix_keydefs = { + '<>': [''], + } + + menudefs = [ + ('edit', [ + ('E_xpand word', '<>'), + ]), + ] + + wordchars = string.letters + string.digits + "_" + + def __init__(self, editwin): + self.text = editwin.text + self.text.wordlist = None # XXX what is this? + self.state = None + + def expand_word_event(self, event): + curinsert = self.text.index("insert") + curline = self.text.get("insert linestart", "insert lineend") + if not self.state: + words = self.getwords() + index = 0 + else: + words, index, insert, line = self.state + if insert != curinsert or line != curline: + words = self.getwords() + index = 0 + if not words: + self.text.bell() + return "break" + word = self.getprevword() + self.text.delete("insert - %d chars" % len(word), "insert") + newword = words[index] + index = (index + 1) % len(words) + if index == 0: + self.text.bell() # Warn we cycled around + self.text.insert("insert", newword) + curinsert = self.text.index("insert") + curline = self.text.get("insert linestart", "insert lineend") + self.state = words, index, curinsert, curline + return "break" + + def getwords(self): + word = self.getprevword() + if not word: + return [] + before = self.text.get("1.0", "insert wordstart") + wbefore = re.findall(r"\b" + word + r"\w+\b", before) + del before + after = self.text.get("insert wordend", "end") + wafter = re.findall(r"\b" + word + r"\w+\b", after) + del after + if not wbefore and not wafter: + return [] + words = [] + dict = {} + # search backwards through words before + wbefore.reverse() + for w in wbefore: + if dict.get(w): + continue + words.append(w) + dict[w] = w + # search onwards through words after + for w in wafter: + if dict.get(w): + continue + words.append(w) + dict[w] = w + words.append(word) + return words + + def getprevword(self): + line = self.text.get("insert linestart", "insert") + i = len(line) + while i > 0 and line[i-1] in self.wordchars: + i = i-1 + return line[i:] diff --git a/Lib/idlelib/AutoIndent.py b/Lib/idlelib/AutoIndent.py new file mode 100644 index 0000000..6d38481 --- /dev/null +++ b/Lib/idlelib/AutoIndent.py @@ -0,0 +1,554 @@ +import string +#from Tkinter import TclError +#import tkMessageBox +#import tkSimpleDialog + +###$ event <> +###$ win +###$ win +###$ unix +###$ unix + +###$ event <> +###$ win +###$ unix +###$ unix + +###$ event <> +###$ win +###$ unix +###$ unix + +###$ event <> +###$ win +###$ unix + +###$ event <> +###$ win +###$ unix + +###$ event <> +###$ win +###$ unix + +###$ event <> +###$ win +###$ unix + +import PyParse + +class AutoIndent: + + menudefs = [ + ('format', [ # /s/edit/format dscherer@cmu.edu + None, + ('_Indent region', '<>'), + ('_Dedent region', '<>'), + ('Comment _out region', '<>'), + ('U_ncomment region', '<>'), + ('Tabify region', '<>'), + ('Untabify region', '<>'), + ('Toggle tabs', '<>'), + ('New indent width', '<>'), + ]), + ] + + keydefs = { + '<>': [''], + '<>': ['', ''], + '<>': [''] + } + + windows_keydefs = { + '<>': [''], + '<>': ['', # dscherer@cmu.edu + ''], + '<>': [''], + '<>': [''], + '<>': [''], + '<>': [''], + '<>': [''], + '<>': [''], + } + + unix_keydefs = { + '<>': ['', + '', + ''], + '<>': ['', + '', + ''], + '<>': ['', ''], + '<>': ['', ''], + '<>': ['', ''], + '<>': ['', ''], + '<>': [''], + '<>': [''], + } + + # usetabs true -> literal tab characters are used by indent and + # dedent cmds, possibly mixed with spaces if + # indentwidth is not a multiple of tabwidth + # false -> tab characters are converted to spaces by indent + # and dedent cmds, and ditto TAB keystrokes + # indentwidth is the number of characters per logical indent level. + # tabwidth is the display width of a literal tab character. + # CAUTION: telling Tk to use anything other than its default + # tab setting causes it to use an entirely different tabbing algorithm, + # treating tab stops as fixed distances from the left margin. + # Nobody expects this, so for now tabwidth should never be changed. + usetabs = 1 + indentwidth = 4 + tabwidth = 8 # for IDLE use, must remain 8 until Tk is fixed + + # If context_use_ps1 is true, parsing searches back for a ps1 line; + # else searches for a popular (if, def, ...) Python stmt. + context_use_ps1 = 0 + + # When searching backwards for a reliable place to begin parsing, + # first start num_context_lines[0] lines back, then + # num_context_lines[1] lines back if that didn't work, and so on. + # The last value should be huge (larger than the # of lines in a + # conceivable file). + # Making the initial values larger slows things down more often. + num_context_lines = 50, 500, 5000000 + + def __init__(self, editwin): + self.editwin = editwin + self.text = editwin.text + + def config(self, **options): + for key, value in options.items(): + if key == 'usetabs': + self.usetabs = value + elif key == 'indentwidth': + self.indentwidth = value + elif key == 'tabwidth': + self.tabwidth = value + elif key == 'context_use_ps1': + self.context_use_ps1 = value + else: + raise KeyError, "bad option name: %s" % `key` + + # If ispythonsource and guess are true, guess a good value for + # indentwidth based on file content (if possible), and if + # indentwidth != tabwidth set usetabs false. + # In any case, adjust the Text widget's view of what a tab + # character means. + + def set_indentation_params(self, ispythonsource, guess=1): + if guess and ispythonsource: + i = self.guess_indent() + if 2 <= i <= 8: + self.indentwidth = i + if self.indentwidth != self.tabwidth: + self.usetabs = 0 + + self.editwin.set_tabwidth(self.tabwidth) + + def smart_backspace_event(self, event): + text = self.text + first, last = self.editwin.get_selection_indices() + if first and last: + text.delete(first, last) + text.mark_set("insert", first) + return "break" + # Delete whitespace left, until hitting a real char or closest + # preceding virtual tab stop. + chars = text.get("insert linestart", "insert") + if chars == '': + if text.compare("insert", ">", "1.0"): + # easy: delete preceding newline + text.delete("insert-1c") + else: + text.bell() # at start of buffer + return "break" + if chars[-1] not in " \t": + # easy: delete preceding real char + text.delete("insert-1c") + return "break" + # Ick. It may require *inserting* spaces if we back up over a + # tab character! This is written to be clear, not fast. + expand, tabwidth = string.expandtabs, self.tabwidth + have = len(expand(chars, tabwidth)) + assert have > 0 + want = int((have - 1) / self.indentwidth) * self.indentwidth + ncharsdeleted = 0 + while 1: + chars = chars[:-1] + ncharsdeleted = ncharsdeleted + 1 + have = len(expand(chars, tabwidth)) + if have <= want or chars[-1] not in " \t": + break + text.undo_block_start() + text.delete("insert-%dc" % ncharsdeleted, "insert") + if have < want: + text.insert("insert", ' ' * (want - have)) + text.undo_block_stop() + return "break" + + def smart_indent_event(self, event): + # if intraline selection: + # delete it + # elif multiline selection: + # do indent-region & return + # indent one level + text = self.text + first, last = self.editwin.get_selection_indices() + text.undo_block_start() + try: + if first and last: + if index2line(first) != index2line(last): + return self.indent_region_event(event) + text.delete(first, last) + text.mark_set("insert", first) + prefix = text.get("insert linestart", "insert") + raw, effective = classifyws(prefix, self.tabwidth) + if raw == len(prefix): + # only whitespace to the left + self.reindent_to(effective + self.indentwidth) + else: + if self.usetabs: + pad = '\t' + else: + effective = len(string.expandtabs(prefix, + self.tabwidth)) + n = self.indentwidth + pad = ' ' * (n - effective % n) + text.insert("insert", pad) + text.see("insert") + return "break" + finally: + text.undo_block_stop() + + def newline_and_indent_event(self, event): + text = self.text + first, last = self.editwin.get_selection_indices() + text.undo_block_start() + try: + if first and last: + text.delete(first, last) + text.mark_set("insert", first) + line = text.get("insert linestart", "insert") + i, n = 0, len(line) + while i < n and line[i] in " \t": + i = i+1 + if i == n: + # the cursor is in or at leading indentation; just inject + # an empty line at the start + text.insert("insert linestart", '\n') + return "break" + indent = line[:i] + # strip whitespace before insert point + i = 0 + while line and line[-1] in " \t": + line = line[:-1] + i = i+1 + if i: + text.delete("insert - %d chars" % i, "insert") + # strip whitespace after insert point + while text.get("insert") in " \t": + text.delete("insert") + # start new line + text.insert("insert", '\n') + + # adjust indentation for continuations and block + # open/close first need to find the last stmt + lno = index2line(text.index('insert')) + y = PyParse.Parser(self.indentwidth, self.tabwidth) + for context in self.num_context_lines: + startat = max(lno - context, 1) + startatindex = `startat` + ".0" + rawtext = text.get(startatindex, "insert") + y.set_str(rawtext) + bod = y.find_good_parse_start( + self.context_use_ps1, + self._build_char_in_string_func(startatindex)) + if bod is not None or startat == 1: + break + y.set_lo(bod or 0) + c = y.get_continuation_type() + if c != PyParse.C_NONE: + # The current stmt hasn't ended yet. + if c == PyParse.C_STRING: + # inside a string; just mimic the current indent + text.insert("insert", indent) + elif c == PyParse.C_BRACKET: + # line up with the first (if any) element of the + # last open bracket structure; else indent one + # level beyond the indent of the line with the + # last open bracket + self.reindent_to(y.compute_bracket_indent()) + elif c == PyParse.C_BACKSLASH: + # if more than one line in this stmt already, just + # mimic the current indent; else if initial line + # has a start on an assignment stmt, indent to + # beyond leftmost =; else to beyond first chunk of + # non-whitespace on initial line + if y.get_num_lines_in_stmt() > 1: + text.insert("insert", indent) + else: + self.reindent_to(y.compute_backslash_indent()) + else: + assert 0, "bogus continuation type " + `c` + return "break" + + # This line starts a brand new stmt; indent relative to + # indentation of initial line of closest preceding + # interesting stmt. + indent = y.get_base_indent_string() + text.insert("insert", indent) + if y.is_block_opener(): + self.smart_indent_event(event) + elif indent and y.is_block_closer(): + self.smart_backspace_event(event) + return "break" + finally: + text.see("insert") + text.undo_block_stop() + + auto_indent = newline_and_indent_event + + # Our editwin provides a is_char_in_string function that works + # with a Tk text index, but PyParse only knows about offsets into + # a string. This builds a function for PyParse that accepts an + # offset. + + def _build_char_in_string_func(self, startindex): + def inner(offset, _startindex=startindex, + _icis=self.editwin.is_char_in_string): + return _icis(_startindex + "+%dc" % offset) + return inner + + def indent_region_event(self, event): + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = classifyws(line, self.tabwidth) + effective = effective + self.indentwidth + lines[pos] = self._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def dedent_region_event(self, event): + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = classifyws(line, self.tabwidth) + effective = max(effective - self.indentwidth, 0) + lines[pos] = self._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def comment_region_event(self, event): + head, tail, chars, lines = self.get_region() + for pos in range(len(lines) - 1): + line = lines[pos] + lines[pos] = '##' + line + self.set_region(head, tail, chars, lines) + + def uncomment_region_event(self, event): + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if not line: + continue + if line[:2] == '##': + line = line[2:] + elif line[:1] == '#': + line = line[1:] + lines[pos] = line + self.set_region(head, tail, chars, lines) + + def tabify_region_event(self, event): + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = classifyws(line, tabwidth) + ntabs, nspaces = divmod(effective, tabwidth) + lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] + self.set_region(head, tail, chars, lines) + + def untabify_region_event(self, event): + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + for pos in range(len(lines)): + lines[pos] = string.expandtabs(lines[pos], tabwidth) + self.set_region(head, tail, chars, lines) + + def toggle_tabs_event(self, event): + if self.editwin.askyesno( + "Toggle tabs", + "Turn tabs " + ("on", "off")[self.usetabs] + "?", + parent=self.text): + self.usetabs = not self.usetabs + return "break" + + # XXX this isn't bound to anything -- see class tabwidth comments + def change_tabwidth_event(self, event): + new = self._asktabwidth() + if new != self.tabwidth: + self.tabwidth = new + self.set_indentation_params(0, guess=0) + return "break" + + def change_indentwidth_event(self, event): + new = self.editwin.askinteger( + "Indent width", + "New indent width (1-16)", + parent=self.text, + initialvalue=self.indentwidth, + minvalue=1, + maxvalue=16) + if new and new != self.indentwidth: + self.indentwidth = new + return "break" + + def get_region(self): + text = self.text + first, last = self.editwin.get_selection_indices() + if first and last: + head = text.index(first + " linestart") + tail = text.index(last + "-1c lineend +1c") + else: + head = text.index("insert linestart") + tail = text.index("insert lineend +1c") + chars = text.get(head, tail) + lines = string.split(chars, "\n") + return head, tail, chars, lines + + def set_region(self, head, tail, chars, lines): + text = self.text + newchars = string.join(lines, "\n") + if newchars == chars: + text.bell() + return + text.tag_remove("sel", "1.0", "end") + text.mark_set("insert", head) + text.undo_block_start() + text.delete(head, tail) + text.insert(head, newchars) + text.undo_block_stop() + text.tag_add("sel", head, "insert") + + # Make string that displays as n leading blanks. + + def _make_blanks(self, n): + if self.usetabs: + ntabs, nspaces = divmod(n, self.tabwidth) + return '\t' * ntabs + ' ' * nspaces + else: + return ' ' * n + + # Delete from beginning of line to insert point, then reinsert + # column logical (meaning use tabs if appropriate) spaces. + + def reindent_to(self, column): + text = self.text + text.undo_block_start() + if text.compare("insert linestart", "!=", "insert"): + text.delete("insert linestart", "insert") + if column: + text.insert("insert", self._make_blanks(column)) + text.undo_block_stop() + + def _asktabwidth(self): + return self.editwin.askinteger( + "Tab width", + "Spaces per tab?", + parent=self.text, + initialvalue=self.tabwidth, + minvalue=1, + maxvalue=16) or self.tabwidth + + # Guess indentwidth from text content. + # Return guessed indentwidth. This should not be believed unless + # it's in a reasonable range (e.g., it will be 0 if no indented + # blocks are found). + + def guess_indent(self): + opener, indented = IndentSearcher(self.text, self.tabwidth).run() + if opener and indented: + raw, indentsmall = classifyws(opener, self.tabwidth) + raw, indentlarge = classifyws(indented, self.tabwidth) + else: + indentsmall = indentlarge = 0 + return indentlarge - indentsmall + +# "line.col" -> line, as an int +def index2line(index): + return int(float(index)) + +# Look at the leading whitespace in s. +# Return pair (# of leading ws characters, +# effective # of leading blanks after expanding +# tabs to width tabwidth) + +def classifyws(s, tabwidth): + raw = effective = 0 + for ch in s: + if ch == ' ': + raw = raw + 1 + effective = effective + 1 + elif ch == '\t': + raw = raw + 1 + effective = (effective / tabwidth + 1) * tabwidth + else: + break + return raw, effective + +import tokenize +_tokenize = tokenize +del tokenize + +class IndentSearcher: + + # .run() chews over the Text widget, looking for a block opener + # and the stmt following it. Returns a pair, + # (line containing block opener, line containing stmt) + # Either or both may be None. + + def __init__(self, text, tabwidth): + self.text = text + self.tabwidth = tabwidth + self.i = self.finished = 0 + self.blkopenline = self.indentedline = None + + def readline(self): + if self.finished: + return "" + i = self.i = self.i + 1 + mark = `i` + ".0" + if self.text.compare(mark, ">=", "end"): + return "" + return self.text.get(mark, mark + " lineend+1c") + + def tokeneater(self, type, token, start, end, line, + INDENT=_tokenize.INDENT, + NAME=_tokenize.NAME, + OPENERS=('class', 'def', 'for', 'if', 'try', 'while')): + if self.finished: + pass + elif type == NAME and token in OPENERS: + self.blkopenline = line + elif type == INDENT and self.blkopenline: + self.indentedline = line + self.finished = 1 + + def run(self): + save_tabsize = _tokenize.tabsize + _tokenize.tabsize = self.tabwidth + try: + try: + _tokenize.tokenize(self.readline, self.tokeneater) + except _tokenize.TokenError: + # since we cut off the tokenizer early, we can trigger + # spurious errors + pass + finally: + _tokenize.tabsize = save_tabsize + return self.blkopenline, self.indentedline diff --git a/Lib/idlelib/Bindings.py b/Lib/idlelib/Bindings.py new file mode 100644 index 0000000..33c6c44 --- /dev/null +++ b/Lib/idlelib/Bindings.py @@ -0,0 +1,67 @@ +# This file defines the menu contents and key bindings. Note that +# there is additional configuration information in the EditorWindow +# class (and subclasses): the menus are created there based on the +# menu_specs (class) variable, and menus not created are silently +# skipped by the code here. This makes it possible to define the +# Debug menu here, which is only present in the PythonShell window. + +# changes by dscherer@cmu.edu: +# - Python shell moved to 'Run' menu +# - "Help" renamed to "IDLE Help" to distinguish from Python help. +# The distinction between the environment and the language is dim +# or nonexistent in a novice's mind. +# - Silly advice added + +import sys +import string +from keydefs import * + +menudefs = [ + # underscore prefixes character to underscore + ('file', [ + ('_New window', '<>'), + ('_Open...', '<>'), + ('Open _module...', '<>'), + ('Class _browser', '<>'), + ('_Path browser', '<>'), + None, + ('_Save', '<>'), + ('Save _As...', '<>'), + ('Save Co_py As...', '<>'), + None, + ('_Close', '<>'), + ('E_xit', '<>'), + ]), + ('edit', [ + ('_Undo', '<>'), + ('_Redo', '<>'), + None, + ('Cu_t', '<>'), + ('_Copy', '<>'), + ('_Paste', '<>'), + ('Select _All', '<>'), + ]), + ('run',[ + ('Python shell', '<>'), + ]), + ('debug', [ + ('_Go to file/line', '<>'), + ('_Stack viewer', '<>'), + ('!_Debugger', '<>'), + ('!_Auto-open stack viewer', '<>' ), + ]), + ('help', [ + ('_IDLE Help...', '<>'), + ('Python _Documentation...', '<>'), + ('_Advice...', '<>'), + None, + ('_About IDLE...', '<>'), + ]), +] + +if sys.platform == 'win32': + default_keydefs = windows_keydefs +else: + default_keydefs = unix_keydefs + +del sys diff --git a/Lib/idlelib/CallTipWindow.py b/Lib/idlelib/CallTipWindow.py new file mode 100644 index 0000000..cbeab8c --- /dev/null +++ b/Lib/idlelib/CallTipWindow.py @@ -0,0 +1,71 @@ +# A CallTip window class for Tkinter/IDLE. +# After ToolTip.py, which uses ideas gleaned from PySol + +# Used by the CallTips IDLE extension. +import os +from Tkinter import * + +class CallTip: + + def __init__(self, widget): + self.widget = widget + self.tipwindow = None + self.id = None + self.x = self.y = 0 + + def showtip(self, text): + self.text = text + if self.tipwindow or not self.text: + return + self.widget.see("insert") + x, y, cx, cy = self.widget.bbox("insert") + x = x + self.widget.winfo_rootx() + 2 + y = y + cy + self.widget.winfo_rooty() + self.tipwindow = tw = Toplevel(self.widget) + tw.wm_overrideredirect(1) + tw.wm_geometry("+%d+%d" % (x, y)) + label = Label(tw, text=self.text, justify=LEFT, + background="#ffffe0", relief=SOLID, borderwidth=1, + font = self.widget['font']) + label.pack() + + def hidetip(self): + tw = self.tipwindow + self.tipwindow = None + if tw: + tw.destroy() + + +############################### +# +# Test Code +# +class container: # Conceptually an editor_window + def __init__(self): + root = Tk() + text = self.text = Text(root) + text.pack(side=LEFT, fill=BOTH, expand=1) + text.insert("insert", "string.split") + root.update() + self.calltip = CallTip(text) + + text.event_add("<>", "(") + text.event_add("<>", ")") + text.bind("<>", self.calltip_show) + text.bind("<>", self.calltip_hide) + + text.focus_set() + # root.mainloop() # not in idle + + def calltip_show(self, event): + self.calltip.showtip("Hello world") + + def calltip_hide(self, event): + self.calltip.hidetip() + +def main(): + # Test code + c=container() + +if __name__=='__main__': + main() diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py new file mode 100644 index 0000000..04eccde --- /dev/null +++ b/Lib/idlelib/CallTips.py @@ -0,0 +1,190 @@ +# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that +# displays parameter information as you open parens. + +import string +import sys +import types + +class CallTips: + + menudefs = [ + ] + + keydefs = { + '<>': [''], + '<>': [''], + '<>': [''], + '<>': ['', ''], + } + + windows_keydefs = { + } + + unix_keydefs = { + } + + def __init__(self, editwin): + self.editwin = editwin + self.text = editwin.text + self.calltip = None + if hasattr(self.text, "make_calltip_window"): + self._make_calltip_window = self.text.make_calltip_window + else: + self._make_calltip_window = self._make_tk_calltip_window + + def close(self): + self._make_calltip_window = None + + # Makes a Tk based calltip window. Used by IDLE, but not Pythonwin. + # See __init__ above for how this is used. + def _make_tk_calltip_window(self): + import CallTipWindow + return CallTipWindow.CallTip(self.text) + + def _remove_calltip_window(self): + if self.calltip: + self.calltip.hidetip() + self.calltip = None + + def paren_open_event(self, event): + self._remove_calltip_window() + arg_text = get_arg_text(self.get_object_at_cursor()) + if arg_text: + self.calltip_start = self.text.index("insert") + self.calltip = self._make_calltip_window() + self.calltip.showtip(arg_text) + return "" #so the event is handled normally. + + def paren_close_event(self, event): + # Now just hides, but later we should check if other + # paren'd expressions remain open. + self._remove_calltip_window() + return "" #so the event is handled normally. + + def check_calltip_cancel_event(self, event): + if self.calltip: + # If we have moved before the start of the calltip, + # or off the calltip line, then cancel the tip. + # (Later need to be smarter about multi-line, etc) + if self.text.compare("insert", "<=", self.calltip_start) or \ + self.text.compare("insert", ">", self.calltip_start + " lineend"): + self._remove_calltip_window() + return "" #so the event is handled normally. + + def calltip_cancel_event(self, event): + self._remove_calltip_window() + return "" #so the event is handled normally. + + def get_object_at_cursor(self, + wordchars="._" + string.uppercase + string.lowercase + string.digits): + # XXX - This needs to be moved to a better place + # so the "." attribute lookup code can also use it. + text = self.text + chars = text.get("insert linestart", "insert") + i = len(chars) + while i and chars[i-1] in wordchars: + i = i-1 + word = chars[i:] + if word: + # How is this for a hack! + import sys, __main__ + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + try: + return eval(word, namespace) + except: + pass + return None # Can't find an object. + +def _find_constructor(class_ob): + # Given a class object, return a function object used for the + # constructor (ie, __init__() ) or None if we can't find one. + try: + return class_ob.__init__.im_func + except AttributeError: + for base in class_ob.__bases__: + rc = _find_constructor(base) + if rc is not None: return rc + return None + +def get_arg_text(ob): + # Get a string describing the arguments for the given object. + argText = "" + if ob is not None: + argOffset = 0 + if type(ob)==types.ClassType: + # Look for the highest __init__ in the class chain. + fob = _find_constructor(ob) + if fob is None: + fob = lambda: None + else: + argOffset = 1 + elif type(ob)==types.MethodType: + # bit of a hack for methods - turn it into a function + # but we drop the "self" param. + fob = ob.im_func + argOffset = 1 + else: + fob = ob + # Try and build one for Python defined functions + if type(fob) in [types.FunctionType, types.LambdaType]: + try: + realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount] + defaults = fob.func_defaults or [] + defaults = list(map(lambda name: "=%s" % name, defaults)) + defaults = [""] * (len(realArgs)-len(defaults)) + defaults + items = map(lambda arg, dflt: arg+dflt, realArgs, defaults) + if fob.func_code.co_flags & 0x4: + items.append("...") + if fob.func_code.co_flags & 0x8: + items.append("***") + argText = string.join(items , ", ") + argText = "(%s)" % argText + except: + pass + # See if we can use the docstring + if hasattr(ob, "__doc__") and ob.__doc__: + pos = string.find(ob.__doc__, "\n") + if pos<0 or pos>70: pos=70 + if argText: argText = argText + "\n" + argText = argText + ob.__doc__[:pos] + + return argText + +################################################# +# +# Test code +# +if __name__=='__main__': + + def t1(): "()" + def t2(a, b=None): "(a, b=None)" + def t3(a, *args): "(a, ...)" + def t4(*args): "(...)" + def t5(a, *args): "(a, ...)" + def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)" + + class TC: + "(a=None, ...)" + def __init__(self, a=None, *b): "(a=None, ...)" + def t1(self): "()" + def t2(self, a, b=None): "(a, b=None)" + def t3(self, a, *args): "(a, ...)" + def t4(self, *args): "(...)" + def t5(self, a, *args): "(a, ...)" + def t6(self, a, b=None, *args, **kw): "(a, b=None, ..., ***)" + + def test( tests ): + failed=[] + for t in tests: + expected = t.__doc__ + "\n" + t.__doc__ + if get_arg_text(t) != expected: + failed.append(t) + print "%s - expected %s, but got %s" % (t, `expected`, `get_arg_text(t)`) + print "%d of %d tests failed" % (len(failed), len(tests)) + + tc = TC() + tests = t1, t2, t3, t4, t5, t6, \ + TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6 + + test(tests) diff --git a/Lib/idlelib/ChangeLog b/Lib/idlelib/ChangeLog new file mode 100644 index 0000000..b853a34 --- /dev/null +++ b/Lib/idlelib/ChangeLog @@ -0,0 +1,1017 @@ +Tue Feb 15 18:08:19 2000 Guido van Rossum + + * NEWS.txt: Notice status bar and stack viewer. + + * EditorWindow.py: Support for Moshe's status bar. + + * MultiStatusBar.py: Status bar code -- by Moshe Zadka. + + * OldStackViewer.py: + Adding the old stack viewer implementation back, for the debugger. + + * StackViewer.py: New stack viewer, uses a tree widget. + (XXX: the debugger doesn't yet use this.) + + * WindowList.py: + Correct a typo and remove an unqualified except that was hiding the error. + + * ClassBrowser.py: Add an XXX comment about the ClassBrowser AIP. + + * ChangeLog: Updated change log. + + * NEWS.txt: News update. Probably incomplete; what else is new? + + * README.txt: + Updated for pending IDLE 0.5 release (still very rough -- just getting + it out in a more convenient format than CVS). + + * TODO.txt: Tiny addition. + +Thu Sep 9 14:16:02 1999 Guido van Rossum + + * TODO.txt: A few new TODO entries. + +Thu Aug 26 23:06:22 1999 Guido van Rossum + + * Bindings.py: Add Python Documentation entry to Help menu. + + * EditorWindow.py: + Find the help.txt file relative to __file__ or ".", not in sys.path. + (Suggested by Moshe Zadka, but implemented differently.) + + Add <> event which, on Unix, brings up Netscape pointing + to http://www.python.doc/current/ (a local copy would be nice but its + location can't be predicted). Windows solution TBD. + +Wed Aug 11 14:55:43 1999 Guido van Rossum + + * TreeWidget.py: + Moshe noticed an inconsistency in his comment, so I'm rephrasing it to + be clearer. + + * TreeWidget.py: + Patch inspired by Moshe Zadka to search for the Icons directory in the + same directory as __file__, rather than searching for it along sys.path. + This works better when idle is a package. + +Thu Jul 15 13:11:02 1999 Guido van Rossum + + * TODO.txt: New wishes. + +Sat Jul 10 13:17:35 1999 Guido van Rossum + + * IdlePrefs.py: + Make the color for stderr red (i.e. the standard warning/danger/stop + color) rather than green. Suggested by Sam Schulenburg. + +Fri Jun 25 17:26:34 1999 Guido van Rossum + + * PyShell.py: Close debugger when closing. This may break a cycle. + + * Debugger.py: Break cycle on close. + + * ClassBrowser.py: Destroy the tree when closing. + + * TreeWidget.py: Add destroy() method to recursively destroy a tree. + + * PyShell.py: Extend _close() to break cycles. + Break some other cycles too (and destroy the root when done). + + * EditorWindow.py: + Add _close() method that does the actual cleanup (close() asks the + user what they want first if there's unsaved stuff, and may cancel). + It closes more than before. + + Add unload_extensions() method to unload all extensions; called from + _close(). It calls an extension's close() method if it has one. + + * Percolator.py: Add close() method that breaks cycles. + + * WidgetRedirector.py: Add unregister() method. + Unregister everything at closing. + Don't call close() in __del__, rely on explicit call to close(). + + * IOBinding.py, FormatParagraph.py, CallTips.py: + Add close() method that breaks a cycle. + +Fri Jun 11 15:03:00 1999 Guido van Rossum + + * AutoIndent.py, EditorWindow.py, FormatParagraph.py: + Tim Peters smart.patch: + + EditorWindow.py: + + + Added get_tabwidth & set_tabwidth "virtual text" methods, that get/set the + widget's view of what a tab means. + + + Moved TK_TABWIDTH_DEFAULT here from AutoIndent. + + + Renamed Mark's get_selection_index to get_selection_indices (sorry, Mark, + but the name was plain wrong ). + + FormatParagraph.py: renamed use of get_selection_index. + + AutoIndent.py: + + + Moved TK_TABWIDTH_DEFAULT to EditorWindow. + + + Rewrote set_indentation_params to use new VTW get/set_tabwidth methods. + + + Changed smart_backspace_event to delete whitespace back to closest + preceding virtual tab stop or real character (note that this may require + inserting characters if backspacing over a tab!). + + + Nuked almost references to the selection tag, in favor of using + get_selection_indices. The sole exception is in set_region, for which no + "set_selection" abstraction has yet been agreed upon. + + + Had too much fun using the spiffy new features of the format-paragraph + cmd. + +Thu Jun 10 17:48:02 1999 Guido van Rossum + + * FormatParagraph.py: + Code by Mark Hammond to format paragraphs embedded in comments. + Read the comments (which I reformatted using the new feature :-) + for some limitations. + + * EditorWindow.py: + Added abstraction get_selection_index() (Mark Hammond). Also + reformatted some comment blocks to show off a cool feature I'm about + to check in next. + + * ClassBrowser.py: + Adapt to the new pyclbr's support of listing top-level functions. If + this functionality is not present (e.g. when used with a vintage + Python 1.5.2 installation) top-level functions are not listed. + + (Hmm... Any distribution of IDLE 0.5 should probably include a copy + of the new pyclbr.py!) + + * AutoIndent.py: + Fix off-by-one error in Tim's recent change to comment_region(): the + list of lines returned by get_region() contains an empty line at the + end representing the start of the next line, and this shouldn't be + commented out! + + * CallTips.py: + Mark Hammond writes: Here is another change that allows it to work for + class creation - tries to locate an __init__ function. Also updated + the test code to reflect your new "***" change. + + * CallTipWindow.py: + Mark Hammond writes: Tim's suggestion of copying the font for the + CallTipWindow from the text control makes sense, and actually makes + the control look better IMO. + +Wed Jun 9 20:34:57 1999 Guido van Rossum + + * CallTips.py: + Append "..." if the appropriate flag (for varargs) in co_flags is set. + Ditto "***" for kwargs. + +Tue Jun 8 13:06:07 1999 Guido van Rossum + + * ReplaceDialog.py: + Hmm... Tim didn't turn "replace all" into a single undo block. + I think I like it better if it os, so here. + + * ReplaceDialog.py: Tim Peters: made replacement atomic for undo/redo. + + * AutoIndent.py: Tim Peters: + + + Set usetabs=1. Editing pyclbr.py was driving me nuts <0.6 wink>. + usetabs=1 is the Emacs pymode default too, and thanks to indentwidth != + tabwidth magical usetabs disabling, new files are still created with tabs + turned off. The only implication is that if you open a file whose first + indent is a single tab, IDLE will now magically use tabs for that file (and + set indentwidth to 8). Note that the whole scheme doesn't work right for + PythonWin, though, since Windows users typically set tabwidth to 4; Mark + probably has to hide the IDLE algorithm from them (which he already knows). + + + Changed comment_region_event to stick "##" in front of every line. The + "holes" previously left on blank lines were visually confusing (made it + needlessly hard to figure out what to uncomment later). + +Mon Jun 7 15:38:40 1999 Guido van Rossum + + * TreeWidget.py, ObjectBrowser.py: + Remove unnecessary reference to pyclbr from test() code. + + * PyParse.py: Tim Peters: + + Smarter logic for finding a parse synch point. + + Does a half to a fifth the work in normal cases; don't notice the speedup, + but makes more breathing room for other extensions. + + Speeds terrible cases by at least a factor of 10. "Terrible" == e.g. you put + """ at the start of Tkinter.py, undo it, zoom to the bottom, and start + typing in code. Used to take about 8 seconds for ENTER to respond, now some + large fraction of a second. The new code gets indented correctly, despite + that it all remains "string colored" until the colorizer catches up (after + which, ENTER appears instantaneous again). + +Fri Jun 4 19:21:19 1999 Guido van Rossum + + * extend.py: Might as well enable CallTips by default. + If there are too many complaints I'll remove it again or fix it. + +Thu Jun 3 14:32:16 1999 Guido van Rossum + + * AutoIndent.py, EditorWindow.py, PyParse.py: + New offerings by Tim Peters; he writes: + + IDLE is now the first Python editor in the Universe not confused by my + doctest.py . + + As threatened, this defines IDLE's is_char_in_string function as a + method of EditorWindow. You just need to define one similarly in + whatever it is you pass as editwin to AutoIndent; looking at the + EditorWindow.py part of the patch should make this clear. + + * GrepDialog.py: Enclose pattern in quotes in status message. + + * CallTips.py: + Mark Hammond fixed some comments and improved the way the tip text is + constructed. + +Wed Jun 2 18:18:57 1999 Guido van Rossum + + * CallTips.py: + My fix to Mark's code: restore the universal check on . + Always cancel on or . + + * CallTips.py: + A version that Mark Hammond posted to the newsgroup. Has some newer + stuff for getting the tip. Had to fix the Key-( and Key-) events + for Unix. Will have to re-apply my patch for catching KeyRelease and + ButtonRelease events. + + * CallTipWindow.py, CallTips.py: + Call tips by Mark Hammond (plus tiny fix by me.) + + * IdleHistory.py: + Changes by Mark Hammond: (1) support optional output_sep argument to + the constructor so he can eliminate the sys.ps2 that PythonWin leaves + in the source; (2) remove duplicate history items. + + * AutoIndent.py: + Changes by Mark Hammond to allow using IDLE extensions in PythonWin as + well: make three dialog routines instance variables. + + * EditorWindow.py: + Change by Mark Hammond to allow using IDLE extensions in PythonWin as + well: make three dialog routines instance variables. + +Tue Jun 1 20:06:44 1999 Guido van Rossum + + * AutoIndent.py: Hah! A fix of my own to Tim's code! + Unix bindings for <> and <> were + missing, and somehow that meant the events were never generated, + even though they were in the menu. The new Unix bindings are now + the same as the Windows bindings (M-t and M-u). + + * AutoIndent.py, PyParse.py, PyShell.py: Tim Peters again: + + The new version (attached) is fast enough all the time in every real module + I have . You can make it slow by, e.g., creating an open list with + 5,000 90-character identifiers (+ trailing comma) each on its own line, then + adding an item to the end -- but that still consumes less than a second on + my P5-166. Response time in real code appears instantaneous. + + Fixed some bugs. + + New feature: when hitting ENTER and the cursor is beyond the line's leading + indentation, whitespace is removed on both sides of the cursor; before + whitespace was removed only on the left; e.g., assuming the cursor is + between the comma and the space: + + def something(arg1, arg2): + ^ cursor to the left of here, and hit ENTER + arg2): # new line used to end up here + arg2): # but now lines up the way you expect + + New hack: AutoIndent has grown a context_use_ps1 Boolean config option, + defaulting to 0 (false) and set to 1 (only) by PyShell. Reason: handling + the fancy stuff requires looking backward for a parsing synch point; ps1 + lines are the only sensible thing to look for in a shell window, but are a + bad thing to look for in a file window (ps1 lines show up in my module + docstrings often). PythonWin's shell should set this true too. + + Persistent problem: strings containing def/class can still screw things up + completely. No improvement. Simplest workaround is on the user's head, and + consists of inserting e.g. + + def _(): pass + + (or any other def/class) after the end of the multiline string that's + screwing them up. This is especially irksome because IDLE's syntax coloring + is *not* confused, so when this happens the colors don't match the + indentation behavior they see. + + * AutoIndent.py: Tim Peters again: + + [Tim, after adding some bracket smarts to AutoIndent.py] + > ... + > What it can't possibly do without reparsing large gobs of text is + > suggest a reasonable indent level after you've *closed* a bracket + > left open on some previous line. + > ... + + The attached can, and actually fast enough to use -- most of the time. The + code is tricky beyond belief to achieve that, but it works so far; e.g., + + return len(string.expandtabs(str[self.stmt_start : + ^ indents to caret + i], + ^ indents to caret + self.tabwidth)) + 1 + ^ indents to caret + + It's about as smart as pymode now, wrt both bracket and backslash + continuation rules. It does require reparsing large gobs of text, and if it + happens to find something that looks like a "def" or "class" or sys.ps1 + buried in a multiline string, but didn't suck up enough preceding text to + see the start of the string, it's completely hosed. I can't repair that -- + it's just too slow to reparse from the start of the file all the time. + + AutoIndent has grown a new num_context_lines tuple attribute that controls + how far to look back, and-- like other params --this could/should be made + user-overridable at startup and per-file on the fly. + + * PyParse.py: New file by Tim Peters: + + One new file in the attached, PyParse.py. The LineStudier (whatever it was + called ) class was removed from AutoIndent; PyParse subsumes its + functionality. + + * AutoIndent.py: Tim Peters keeps revising this module (more to come): + + Removed "New tabwidth" menu binding. + + Added "a tab means how many spaces?" dialog to block tabify and untabify. I + think prompting for this is good now: they're usually at-most-once-per-file + commands, and IDLE can't let them change tabwidth from the Tk default + anymore, so IDLE can no longer presume to have any idea what a tab means. + + Irony: for the purpose of keeping comments aligned via tabs, Tk's + non-default approach is much nicer than the Emacs/Notepad/Codewright/vi/etc + approach. + + * EditorWindow.py: + 1. Catch NameError on import (could be raised by case mismatch on Windows). + 2. No longer need to reset pyclbr cache and show watch cursor when calling + ClassBrowser -- the ClassBrowser takes care of pyclbr and the TreeWidget + takes care of the watch cursor. + 3. Reset the focus to the current window after error message about class + browser on buffer without filename. + + * Icons/minusnode.gif, Icons/plusnode.gif: Missed a few. + + * ClassBrowser.py, PathBrowser.py: Rewritten based on TreeWidget.py + + * ObjectBrowser.py: Object browser, based on TreeWidget.py. + + * TreeWidget.py: Tree widget done right. + + * ToolTip.py: As yet unused code for tool tips. + + * ScriptBinding.py: + Ensure sys.argv[0] is the script name on Run Script. + + * ZoomHeight.py: Move zoom height functionality to separate function. + + * Icons/folder.gif, Icons/openfolder.gif, Icons/python.gif, Icons/tk.gif: + A few icons used by ../TreeWidget.py and its callers. + + * AutoIndent.py: New version by Tim Peters improves block opening test. + +Fri May 21 04:46:17 1999 Guido van Rossum + + * Attic/History.py, PyShell.py: Rename History to IdleHistory. + Add isatty() to pseudo files. + + * StackViewer.py: Make initial stack viewer wider + + * TODO.txt: New wishes + + * AutoIndent.py, EditorWindow.py, PyShell.py: + Much improved autoindent and handling of tabs, + by Tim Peters. + +Mon May 3 15:49:52 1999 Guido van Rossum + + * AutoIndent.py, EditorWindow.py, FormatParagraph.py, UndoDelegator.py: + Tim Peters writes: + + I'm still unsure, but couldn't stand the virtual event trickery so tried a + different sin (adding undo_block_start/stop methods to the Text instance in + EditorWindow.py). Like it or not, it's efficient and works . Better + idea? + + Give the attached a whirl. Even if you hate the implementation, I think + you'll like the results. Think I caught all the "block edit" cmds, + including Format Paragraph, plus subtler ones involving smart indents and + backspacing. + + * WidgetRedirector.py: Tim Peters writes: + + [W]hile trying to dope out how redirection works, stumbled into two + possible glitches. In the first, it doesn't appear to make sense to try to + rename a command that's already been destroyed; in the second, the name + "previous" doesn't really bring to mind "ignore the previous value" . + +Fri Apr 30 19:39:25 1999 Guido van Rossum + + * __init__.py: Support for using idle as a package. + + * PathBrowser.py: + Avoid listing files more than once (e.g. foomodule.so has two hits: + once for foo + module.so, once for foomodule + .so). + +Mon Apr 26 22:20:38 1999 Guido van Rossum + + * ChangeLog, ColorDelegator.py, PyShell.py: Tim Peters strikes again: + + Ho ho ho -- that's trickier than it sounded! The colorizer is working with + "line.col" strings instead of Text marks, and the absolute coordinates of + the point of interest can change across the self.update call (voice of + baffled experience, when two quick backspaces no longer fooled it, but a + backspace followed by a quick ENTER did ). + + Anyway, the attached appears to do the trick. CPU usage goes way up when + typing quickly into a long triple-quoted string, but the latency is fine for + me (a relatively fast typist on a relatively slow machine). Most of the + changes here are left over from reducing the # of vrbl names to help me + reason about the logic better; I hope the code is a *little* easier to + +Fri Apr 23 14:01:25 1999 Guido van Rossum + + * EditorWindow.py: + Provide full arguments to __import__ so it works in packagized IDLE. + +Thu Apr 22 23:20:17 1999 Guido van Rossum + + * help.txt: + Bunch of updates necessary due to recent changes; added docs for File + menu, command line and color preferences. + + * Bindings.py: Remove obsolete 'script' menu. + + * TODO.txt: Several wishes fulfilled. + + * OutputWindow.py: + Moved classes OnDemandOutputWindow and PseudoFile here, + from ScriptBinding.py where they are no longer needed. + + * ScriptBinding.py: + Mostly rewritten. Instead of the old Run module and Debug module, + there are two new commands: + + Import module (F5) imports or reloads the module and also adds its + name to the __main__ namespace. This gets executed in the PyShell + window under control of its debug settings. + + Run script (Control-F5) is similar but executes the contents of the + file directly in the __main__ namespace. + + * PyShell.py: Nits: document use of $IDLESTARTUP; display idle version + + * idlever.py: New version to celebrate new command line + + * OutputWindow.py: Added flush(), for completeness. + + * PyShell.py: + A lot of changes to make the command line more useful. You can now do: + idle.py -e file ... -- to edit files + idle.py script arg ... -- to run a script + idle.py -c cmd arg ... -- to run a command + Other options, see also the usage message (also new!) for more details: + -d -- enable debugger + -s -- run $IDLESTARTUP or $PYTHONSTARTUP + -t title -- set Python Shell window's title + sys.argv is set accordingly, unless -e is used. + sys.path is absolutized, and all relevant paths are inserted into it. + + Other changes: + - the environment in which commands are executed is now the + __main__ module + - explicitly save sys.stdout etc., don't restore from sys.__stdout__ + - new interpreter methods execsource(), execfile(), stuffsource() + - a few small nits + + * TODO.txt: + Some more TODO items. Made up my mind about command line args, + Run/Import, __main__. + + * ColorDelegator.py: + Super-elegant patch by Tim Peters that speeds up colorization + dramatically (up to 15 times he claims). Works by reading more than + one line at a time, up to 100-line chunks (starting with one line and + then doubling up to the limit). On a typical machine (e.g. Tim's + P5-166) this doesn't reduce interactive responsiveness in a noticeable + way. + +Wed Apr 21 15:49:34 1999 Guido van Rossum + + * ColorDelegator.py: + Patch by Tim Peters to speed up colorizing of big multiline strings. + +Tue Apr 20 17:32:52 1999 Guido van Rossum + + * extend.txt: + For an event 'foo-bar', the corresponding method must be called + foo_bar_event(). Therefore, fix the references to zoom_height() in + the example. + + * IdlePrefs.py: Restored the original IDLE color scheme. + + * PyShell.py, IdlePrefs.py, ColorDelegator.py, EditorWindow.py: + Color preferences code by Loren Luke (massaged by me somewhat) + + * SearchEngine.py: + Patch by Mark Favas: it fixes the search engine behaviour where an + unsuccessful search wraps around and re-searches that part of the file + between the start of the search and the end of the file - only really + an issue for very large files, but... (also removes a redundant + m.span() call). + +Mon Apr 19 16:26:02 1999 Guido van Rossum + + * TODO.txt: A few wishes are now fulfilled. + + * AutoIndent.py: Tim Peters implements some of my wishes: + + o Makes the tab key intelligently insert spaces when appropriate + (see Help list banter twixt David Ascher and me; idea stolen from + every other editor on earth ). + + o newline_and_indent_event trims trailing whitespace on the old + line (pymode and Codewright). + + o newline_and_indent_event no longer fooled by trailing whitespace or + comment after ":" (pymode, PTUI). + + o newline_and_indent_event now reduces the new line's indentation after + return, break, continue, raise and pass stmts (pymode). + + The last two are easy to fool in the presence of strings & + continuations, but pymode requires Emacs's high-powered C parsing + functions to avoid that in finite time. + +====================================================================== + Python release 1.5.2c1, IDLE version 0.4 +====================================================================== + +Wed Apr 7 18:41:59 1999 Guido van Rossum + + * README.txt, NEWS.txt: New version. + + * idlever.py: Version bump awaiting impending new release. + (Not much has changed :-( ) + +Mon Mar 29 14:52:28 1999 Guido van Rossum + + * ScriptBinding.py, PyShell.py: + At Tim Peters' recommendation, add a dummy flush() method to + PseudoFile. + +Thu Mar 11 23:21:23 1999 Guido van Rossum + + * PathBrowser.py: Don't crash when sys.path contains an empty string. + + * Attic/Outline.py: This file was never supposed to be part of IDLE. + + * PathBrowser.py: + - Don't crash in the case where a superclass is a string instead of a + pyclbr.Class object; this can happen when the superclass is + unrecognizable (to pyclbr), e.g. when module renaming is used. + + - Show a watch cursor when calling pyclbr (since it may take a while + recursively parsing imported modules!). + +Wed Mar 10 05:18:02 1999 Guido van Rossum + + * EditorWindow.py, Bindings.py: Add PathBrowser to File module + + * PathBrowser.py: "Path browser" - 4 scrolled lists displaying: + directories on sys.path + modules in selected directory + classes in selected module + methods of selected class + + Sinlge clicking in a directory, module or class item updates the next + column with info about the selected item. Double clicking in a + module, class or method item opens the file (and selects the clicked + item if it is a class or method). + + I guess eventually I should be using a tree widget for this, but the + ones I've seen don't work well enough, so for now I use the old + Smalltalk or NeXT style multi-column hierarchical browser. + + * MultiScrolledLists.py: + New utility: multiple scrolled lists in parallel + + * ScrolledList.py: - White background. + - Display "(None)" (or text of your choosing) when empty. + - Don't set the focus. + +====================================================================== + Python release 1.5.2b2, IDLE version 0.3 +====================================================================== + +Wed Feb 17 22:47:41 1999 Guido van Rossum + + * NEWS.txt: News in 0.3. + + * README.txt, idlever.py: Bump version to 0.3. + + * EditorWindow.py: + After all, we don't need to call the callbacks ourselves! + + * WindowList.py: + When deleting, call the callbacks *after* deleting the window from our list! + + * EditorWindow.py: + Fix up the Windows menu via the new callback mechanism instead of + depending on menu post commands (which don't work when the menu is + torn off). + + * WindowList.py: + Support callbacks to patch up Windows menus everywhere. + + * ChangeLog: Oh, why not. Checking in the Emacs-generated change log. + +Tue Feb 16 22:34:17 1999 Guido van Rossum + + * ScriptBinding.py: + Only pop up the stack viewer when requested in the Debug menu. + +Mon Feb 8 22:27:49 1999 Guido van Rossum + + * WindowList.py: Don't crash if a window no longer exists. + + * TODO.txt: Restructured a bit. + +Mon Feb 1 23:06:17 1999 Guido van Rossum + + * PyShell.py: Add current dir or paths of file args to sys.path. + + * Debugger.py: Add canonic() function -- for brand new bdb.py feature. + + * StackViewer.py: Protect against accessing an empty stack. + +Fri Jan 29 20:44:45 1999 Guido van Rossum + + * ZoomHeight.py: + Use only the height to decide whether to zoom in or out. + +Thu Jan 28 22:24:30 1999 Guido van Rossum + + * EditorWindow.py, FileList.py: + Make sure the Tcl variables are shared between windows. + + * PyShell.py, EditorWindow.py, Bindings.py: + Move menu/key binding code from Bindings.py to EditorWindow.py, + with changed APIs -- it makes much more sense there. + Also add a new feature: if the first character of a menu label is + a '!', it gets a checkbox. Checkboxes are bound to Boolean Tcl variables + that can be accessed through the new getvar/setvar/getrawvar API; + the variable is named after the event to which the menu is bound. + + * Debugger.py: Add Quit button to the debugger window. + + * SearchDialog.py: + When find_again() finds exactly the current selection, it's a failure. + + * idle.py, Attic/idle: Rename idle -> idle.py + +Mon Jan 18 15:18:57 1999 Guido van Rossum + + * EditorWindow.py, WindowList.py: Only deiconify when iconic. + + * TODO.txt: Misc + +Tue Jan 12 22:14:34 1999 Guido van Rossum + + * testcode.py, Attic/test.py: + Renamed test.py to testcode.py so one can import Python's + test package from inside IDLE. (Suggested by Jack Jansen.) + + * EditorWindow.py, ColorDelegator.py: + Hack to close a window that is colorizing. + + * Separator.py: Vladimir Marangozov's patch: + The separator dances too much and seems to jump by arbitrary amounts + in arbitrary directions when I try to move it for resizing the frames. + This patch makes it more quiet. + +Mon Jan 11 14:52:40 1999 Guido van Rossum + + * TODO.txt: Some requests have been fulfilled. + + * EditorWindow.py: + Set the cursor to a watch when opening the class browser (which may + take quite a while, browsing multiple files). + + Newer, better center() -- but assumes no wrapping. + + * SearchBinding.py: + Got rid of debug print statement in goto_line_event(). + + * ScriptBinding.py: + I think I like it better if it prints the traceback even when it displays + the stack viewer. + + * Debugger.py: Bind ESC to close-window. + + * ClassBrowser.py: Use a HSeparator between the classes and the items. + Make the list of classes wider by default (40 chars). + Bind ESC to close-window. + + * Separator.py: + Separator classes (draggable divider between two panes). + +Sat Jan 9 22:01:33 1999 Guido van Rossum + + * WindowList.py: + Don't traceback when wakeup() is called when the window has been destroyed. + This can happen when a torn-of Windows menu references closed windows. + And Tim Peters claims that the Windows menu is his favorite to tear off... + + * EditorWindow.py: Allow tearing off of the Windows menu. + + * StackViewer.py: Close on ESC. + + * help.txt: Updated a bunch of things (it was mostly still 0.1!) + + * extend.py: Added ScriptBinding to standard bindings. + + * ScriptBinding.py: + This now actually works. See doc string. It can run a module (i.e. + import or reload) or debug it (same with debugger control). Output + goes to a fresh output window, only created when needed. + +====================================================================== + Python release 1.5.2b1, IDLE version 0.2 +====================================================================== + +Fri Jan 8 17:26:02 1999 Guido van Rossum + + * README.txt, NEWS.txt: What's new in this release. + + * Bindings.py, PyShell.py: + Paul Prescod's patches to allow the stack viewer to pop up when a + traceback is printed. + +Thu Jan 7 00:12:15 1999 Guido van Rossum + + * FormatParagraph.py: + Change paragraph width limit to 70 (like Emacs M-Q). + + * README.txt: + Separating TODO from README. Slight reformulation of features. No + exact release date. + + * TODO.txt: Separating TODO from README. + +Mon Jan 4 21:19:09 1999 Guido van Rossum + + * FormatParagraph.py: + Hm. There was a boundary condition error at the end of the file too. + + * SearchBinding.py: Hm. Add Unix binding for replace, too. + + * keydefs.py: Ran eventparse.py again. + + * FormatParagraph.py: Added Unix Meta-q key binding; + fix find_paragraph when at start of file. + + * AutoExpand.py: Added Meta-/ binding for Unix as alt for Alt-/. + + * SearchBinding.py: + Add unix binding for grep (otherwise the menu entry doesn't work!) + + * ZoomHeight.py: Adjusted Unix height to work with fvwm96. :=( + + * GrepDialog.py: Need to import sys! + + * help.txt, extend.txt, README.txt: Formatted some paragraphs + + * extend.py, FormatParagraph.py: + Add new extension to reformat a (text) paragraph. + + * ZoomHeight.py: Typo in Win specific height setting. + +Sun Jan 3 00:47:35 1999 Guido van Rossum + + * AutoIndent.py: Added something like Tim Peters' backspace patch. + + * ZoomHeight.py: Adapted to Unix (i.e., more hardcoded constants). + +Sat Jan 2 21:28:54 1999 Guido van Rossum + + * keydefs.py, idlever.py, idle.pyw, idle.bat, help.txt, extend.txt, extend.py, eventparse.py, ZoomHeight.py, WindowList.py, UndoDelegator.py, StackViewer.py, SearchEngine.py, SearchDialogBase.py, SearchDialog.py, ScrolledList.py, SearchBinding.py, ScriptBinding.py, ReplaceDialog.py, Attic/README, README.txt, PyShell.py, Attic/PopupMenu.py, OutputWindow.py, IOBinding.py, Attic/HelpWindow.py, History.py, GrepDialog.py, FileList.py, FrameViewer.py, EditorWindow.py, Debugger.py, Delegator.py, ColorDelegator.py, Bindings.py, ClassBrowser.py, AutoExpand.py, AutoIndent.py: + Checking in IDLE 0.2. + + Much has changed -- too much, in fact, to write down. + The big news is that there's a standard way to write IDLE extensions; + see extend.txt. Some sample extensions have been provided, and + some existing code has been converted to extensions. Probably the + biggest new user feature is a new search dialog with more options, + search and replace, and even search in files (grep). + + This is exactly as downloaded from my laptop after returning + from the holidays -- it hasn't even been tested on Unix yet. + +Fri Dec 18 15:52:54 1998 Guido van Rossum + + * FileList.py, ClassBrowser.py: + Fix the class browser to work even when the file is not on sys.path. + +Tue Dec 8 20:39:36 1998 Guido van Rossum + + * Attic/turtle.py: Moved to Python 1.5.2/Lib + +Fri Nov 27 03:19:20 1998 Guido van Rossum + + * help.txt: Typo + + * EditorWindow.py, FileList.py: Support underlining of menu labels + + * Bindings.py: + New approach, separate tables for menus (platform-independent) and key + definitions (platform-specific), and generating accelerator strings + automatically from the key definitions. + +Mon Nov 16 18:37:42 1998 Guido van Rossum + + * Attic/README: Clarify portability and main program. + + * Attic/README: Added intro for 0.1 release and append Grail notes. + +Mon Oct 26 18:49:00 1998 Guido van Rossum + + * Attic/turtle.py: root is now a global called _root + +Sat Oct 24 16:38:38 1998 Guido van Rossum + + * Attic/turtle.py: Raise the root window on reset(). + Different action on WM_DELETE_WINDOW is more likely to do the right thing, + allowing us to destroy old windows. + + * Attic/turtle.py: + Split the goto() function in two: _goto() is the internal one, + using Canvas coordinates, and goto() uses turtle coordinates + and accepts variable argument lists. + + * Attic/turtle.py: Cope with destruction of the window + + * Attic/turtle.py: Turtle graphics + + * Debugger.py: Use of Breakpoint class should be bdb.Breakpoint. + +Mon Oct 19 03:33:40 1998 Guido van Rossum + + * SearchBinding.py: + Speed up the search a bit -- don't drag a mark around... + + * PyShell.py: + Change our special entries from to . + Patch linecache.checkcache() to keep our special entries alive. + Add popup menu to all editor windows to set a breakpoint. + + * Debugger.py: + Use and pass through the 'force' flag to set_dict() where appropriate. + Default source and globals checkboxes to false. + Don't interact in user_return(). + Add primitive set_breakpoint() method. + + * ColorDelegator.py: + Raise priority of 'sel' tag so its foreground (on Windows) will take + priority over text colorization (which on Windows is almost the + same color as the selection background). + + Define a tag and color for breakpoints ("BREAK"). + + * Attic/PopupMenu.py: Disable "Open stack viewer" and "help" commands. + + * StackViewer.py: + Add optional 'force' argument (default 0) to load_dict(). + If set, redo the display even if it's the same dict. + +Fri Oct 16 21:10:12 1998 Guido van Rossum + + * StackViewer.py: Do nothing when loading the same dict as before. + + * PyShell.py: Details for debugger interface. + + * Debugger.py: + Restructured and more consistent. Save checkboxes across instantiations. + + * EditorWindow.py, Attic/README, Bindings.py: + Get rid of conflicting ^X binding. Use ^W. + + * Debugger.py, StackViewer.py: + Debugger can now show local and global variables. + + * Debugger.py: Oops + + * Debugger.py, PyShell.py: Better debugger support (show stack etc). + + * Attic/PopupMenu.py: Follow renames in StackViewer module + + * StackViewer.py: + Rename classes to StackViewer (the widget) and StackBrowser (the toplevel). + + * ScrolledList.py: Add close() method + + * EditorWindow.py: Clarify 'Open Module' dialog text + + * StackViewer.py: Restructured into a browser and a widget. + +Thu Oct 15 23:27:08 1998 Guido van Rossum + + * ClassBrowser.py, ScrolledList.py: + Generalized the scrolled list which is the base for the class and + method browser into a separate class in its own module. + + * Attic/test.py: Cosmetic change + + * Debugger.py: Don't show function name if there is none + +Wed Oct 14 03:43:05 1998 Guido van Rossum + + * Debugger.py, PyShell.py: Polish the Debugger GUI a bit. + Closing it now also does the right thing. + +Tue Oct 13 23:51:13 1998 Guido van Rossum + + * Debugger.py, PyShell.py, Bindings.py: + Ad primitive debugger interface (so far it will step and show you the + source, but it doesn't yet show the stack). + + * Attic/README: Misc + + * StackViewer.py: Whoops -- referenced self.top before it was set. + + * help.txt: Added history and completion commands. + + * help.txt: Updated + + * FileList.py: Add class browser functionality. + + * StackViewer.py: + Add a close() method and bind to WM_DELETE_WINDOW protocol + + * PyShell.py: Clear the linecache before printing a traceback + + * Bindings.py: Added class browser binding. + + * ClassBrowser.py: Much improved, much left to do. + + * PyShell.py: Make the return key do what I mean more often. + + * ClassBrowser.py: + Adding the beginnings of a Class browser. Incomplete, yet. + + * EditorWindow.py, Bindings.py: + Add new command, "Open module". You select or type a module name, + and it opens the source. + +Mon Oct 12 23:59:27 1998 Guido van Rossum + + * PyShell.py: Subsume functionality from Popup menu in Debug menu. + Other stuff so the PyShell window can be resurrected from the Windows menu. + + * FileList.py: Get rid of PopUp menu. + Create a simple Windows menu. (Imperfect when Untitled windows exist.) + Add wakeup() method: deiconify, raise, focus. + + * EditorWindow.py: Generalize menu creation. + + * Bindings.py: Add Debug and Help menu items. + + * EditorWindow.py: Added a menu bar to every window. + + * Bindings.py: Add menu configuration to the event configuration. + + * Attic/PopupMenu.py: Pass a root to the help window. + + * SearchBinding.py: + Add parent argument to 'to to line number' dialog box. + +Sat Oct 10 19:15:32 1998 Guido van Rossum + + * StackViewer.py: + Add a label at the top showing (very basic) help for the stack viewer. + Add a label at the bottom showing the exception info. + + * Attic/test.py, Attic/idle: Add Unix main script and test program. + + * idle.pyw, help.txt, WidgetRedirector.py, UndoDelegator.py, StackViewer.py, SearchBinding.py, Attic/README, PyShell.py, Attic/PopupMenu.py, Percolator.py, Outline.py, IOBinding.py, History.py, Attic/HelpWindow.py, FrameViewer.py, FileList.py, EditorWindow.py, Delegator.py, ColorDelegator.py, Bindings.py, AutoIndent.py, AutoExpand.py: + Initial checking of Tk-based Python IDE. + Features: text editor with syntax coloring and undo; + subclassed into interactive Python shell which adds history. + diff --git a/Lib/idlelib/ClassBrowser.py b/Lib/idlelib/ClassBrowser.py new file mode 100644 index 0000000..f440164 --- /dev/null +++ b/Lib/idlelib/ClassBrowser.py @@ -0,0 +1,224 @@ +"""Class browser. + +XXX TO DO: + +- reparse when source changed (maybe just a button would be OK?) + (or recheck on window popup) +- add popup menu with more options (e.g. doc strings, base classes, imports) +- show function argument list? (have to do pattern matching on source) +- should the classes and methods lists also be in the module's menu bar? +- add base classes to class browser tree +""" + +import os +import sys +import string +import pyclbr + +# XXX Patch pyclbr with dummies if it's vintage Python 1.5.2: +if not hasattr(pyclbr, "readmodule_ex"): + pyclbr.readmodule_ex = pyclbr.readmodule +if not hasattr(pyclbr, "Function"): + class Function(pyclbr.Class): + pass + pyclbr.Function = Function + +import PyShell +from WindowList import ListedToplevel +from TreeWidget import TreeNode, TreeItem, ScrolledCanvas + +class ClassBrowser: + + def __init__(self, flist, name, path): + # XXX This API should change, if the file doesn't end in ".py" + # XXX the code here is bogus! + self.name = name + self.file = os.path.join(path[0], self.name + ".py") + self.init(flist) + + def close(self, event=None): + self.top.destroy() + self.node.destroy() + + 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("", self.close) + self.settitle() + top.focus_set() + # create scrolled canvas + sc = ScrolledCanvas(top, bg="white", 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() + + def settitle(self): + self.top.wm_title("Class Browser - " + self.name) + self.top.wm_iconname("Class Browser") + + def rootnode(self): + return ModuleBrowserTreeItem(self.file) + +class ModuleBrowserTreeItem(TreeItem): + + def __init__(self, file): + self.file = file + + def GetText(self): + return os.path.basename(self.file) + + def GetIconName(self): + return "python" + + def GetSubList(self): + sublist = [] + for name in self.listclasses(): + item = ClassBrowserTreeItem(name, self.classes, self.file) + sublist.append(item) + return sublist + + def OnDoubleClick(self): + if os.path.normcase(self.file[-3:]) != ".py": + return + if not os.path.exists(self.file): + return + PyShell.flist.open(self.file) + + def IsExpandable(self): + return os.path.normcase(self.file[-3:]) == ".py" + + def listclasses(self): + dir, file = os.path.split(self.file) + name, ext = os.path.splitext(file) + if os.path.normcase(ext) != ".py": + return [] + try: + dict = pyclbr.readmodule_ex(name, [dir] + sys.path) + except ImportError, msg: + return [] + items = [] + self.classes = {} + for key, cl in dict.items(): + if cl.module == name: + s = key + if cl.super: + supers = [] + for sup in cl.super: + if type(sup) is type(''): + sname = sup + else: + sname = sup.name + if sup.module != cl.module: + sname = "%s.%s" % (sup.module, sname) + supers.append(sname) + s = s + "(%s)" % string.join(supers, ", ") + items.append((cl.lineno, s)) + self.classes[s] = cl + items.sort() + list = [] + for item, s in items: + list.append(s) + return list + +class ClassBrowserTreeItem(TreeItem): + + def __init__(self, name, classes, file): + self.name = name + self.classes = classes + self.file = file + try: + self.cl = self.classes[self.name] + except (IndexError, KeyError): + self.cl = None + self.isfunction = isinstance(self.cl, pyclbr.Function) + + def GetText(self): + if self.isfunction: + return "def " + self.name + "(...)" + else: + return "class " + self.name + + def GetIconName(self): + if self.isfunction: + return "python" + else: + return "folder" + + def IsExpandable(self): + if self.cl: + return not not self.cl.methods + + def GetSubList(self): + if not self.cl: + return [] + sublist = [] + for name in self.listmethods(): + item = MethodBrowserTreeItem(name, self.cl, self.file) + sublist.append(item) + return sublist + + def OnDoubleClick(self): + if not os.path.exists(self.file): + return + edit = PyShell.flist.open(self.file) + if hasattr(self.cl, 'lineno'): + lineno = self.cl.lineno + edit.gotoline(lineno) + + def listmethods(self): + if not self.cl: + return [] + items = [] + for name, lineno in self.cl.methods.items(): + items.append((lineno, name)) + items.sort() + list = [] + for item, name in items: + list.append(name) + return list + +class MethodBrowserTreeItem(TreeItem): + + def __init__(self, name, cl, file): + self.name = name + self.cl = cl + self.file = file + + def GetText(self): + return "def " + self.name + "(...)" + + def GetIconName(self): + return "python" # XXX + + def IsExpandable(self): + return 0 + + def OnDoubleClick(self): + if not os.path.exists(self.file): + return + edit = PyShell.flist.open(self.file) + edit.gotoline(self.cl.methods[self.name]) + +def main(): + try: + file = __file__ + except NameError: + file = sys.argv[0] + if sys.argv[1:]: + file = sys.argv[1] + else: + file = sys.argv[0] + dir, file = os.path.split(file) + name = os.path.splitext(file)[0] + ClassBrowser(PyShell.flist, name, [dir]) + if sys.stdin is sys.__stdin__: + mainloop() + +if __name__ == "__main__": + main() diff --git a/Lib/idlelib/ColorDelegator.py b/Lib/idlelib/ColorDelegator.py new file mode 100644 index 0000000..77edfe8 --- /dev/null +++ b/Lib/idlelib/ColorDelegator.py @@ -0,0 +1,234 @@ +import time +import string +import re +import keyword +from Tkinter import * +from Delegator import Delegator +from IdleConf import idleconf + +#$ event <> +#$ win +#$ unix + +__debug__ = 0 + + +def any(name, list): + return "(?P<%s>" % name + string.join(list, "|") + ")" + +def make_pat(): + kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" + comment = any("COMMENT", [r"#[^\n]*"]) + sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' + sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" + dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' + string = any("STRING", [sq3string, dq3string, sqstring, dqstring]) + return kw + "|" + comment + "|" + string + "|" + any("SYNC", [r"\n"]) + +prog = re.compile(make_pat(), re.S) +idprog = re.compile(r"\s+(\w+)", re.S) + +class ColorDelegator(Delegator): + + def __init__(self): + Delegator.__init__(self) + self.prog = prog + self.idprog = idprog + + def setdelegate(self, delegate): + if self.delegate is not None: + self.unbind("<>") + Delegator.setdelegate(self, delegate) + if delegate is not None: + self.config_colors() + self.bind("<>", self.toggle_colorize_event) + self.notify_range("1.0", "end") + + def config_colors(self): + for tag, cnf in self.tagdefs.items(): + if cnf: + apply(self.tag_configure, (tag,), cnf) + self.tag_raise('sel') + + cconf = idleconf.getsection('Colors') + + tagdefs = { + "COMMENT": cconf.getcolor("comment"), + "KEYWORD": cconf.getcolor("keyword"), + "STRING": cconf.getcolor("string"), + "DEFINITION": cconf.getcolor("definition"), + "SYNC": cconf.getcolor("sync"), + "TODO": cconf.getcolor("todo"), + "BREAK": cconf.getcolor("break"), + # The following is used by ReplaceDialog: + "hit": cconf.getcolor("hit"), + } + + def insert(self, index, chars, tags=None): + index = self.index(index) + self.delegate.insert(index, chars, tags) + self.notify_range(index, index + "+%dc" % len(chars)) + + def delete(self, index1, index2=None): + index1 = self.index(index1) + self.delegate.delete(index1, index2) + self.notify_range(index1) + + after_id = None + allow_colorizing = 1 + colorizing = 0 + + def notify_range(self, index1, index2=None): + self.tag_add("TODO", index1, index2) + if self.after_id: + if __debug__: print "colorizing already scheduled" + return + if self.colorizing: + self.stop_colorizing = 1 + if __debug__: print "stop colorizing" + if self.allow_colorizing: + if __debug__: print "schedule colorizing" + self.after_id = self.after(1, self.recolorize) + + close_when_done = None # Window to be closed when done colorizing + + def close(self, close_when_done=None): + if self.after_id: + after_id = self.after_id + self.after_id = None + if __debug__: print "cancel scheduled recolorizer" + self.after_cancel(after_id) + self.allow_colorizing = 0 + self.stop_colorizing = 1 + if close_when_done: + if not self.colorizing: + close_when_done.destroy() + else: + self.close_when_done = close_when_done + + def toggle_colorize_event(self, event): + if self.after_id: + after_id = self.after_id + self.after_id = None + if __debug__: print "cancel scheduled recolorizer" + self.after_cancel(after_id) + if self.allow_colorizing and self.colorizing: + if __debug__: print "stop colorizing" + self.stop_colorizing = 1 + self.allow_colorizing = not self.allow_colorizing + if self.allow_colorizing and not self.colorizing: + self.after_id = self.after(1, self.recolorize) + if __debug__: + print "auto colorizing turned", self.allow_colorizing and "on" or "off" + return "break" + + def recolorize(self): + self.after_id = None + if not self.delegate: + if __debug__: print "no delegate" + return + if not self.allow_colorizing: + if __debug__: print "auto colorizing is off" + return + if self.colorizing: + if __debug__: print "already colorizing" + return + try: + self.stop_colorizing = 0 + self.colorizing = 1 + if __debug__: print "colorizing..." + t0 = time.clock() + self.recolorize_main() + t1 = time.clock() + if __debug__: print "%.3f seconds" % (t1-t0) + finally: + self.colorizing = 0 + if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"): + if __debug__: print "reschedule colorizing" + self.after_id = self.after(1, self.recolorize) + if self.close_when_done: + top = self.close_when_done + self.close_when_done = None + top.destroy() + + def recolorize_main(self): + next = "1.0" + while 1: + item = self.tag_nextrange("TODO", next) + if not item: + break + head, tail = item + self.tag_remove("SYNC", head, tail) + item = self.tag_prevrange("SYNC", head) + if item: + head = item[1] + else: + head = "1.0" + + chars = "" + next = head + lines_to_get = 1 + ok = 0 + while not ok: + mark = next + next = self.index(mark + "+%d lines linestart" % + lines_to_get) + lines_to_get = min(lines_to_get * 2, 100) + ok = "SYNC" in self.tag_names(next + "-1c") + line = self.get(mark, next) + ##print head, "get", mark, next, "->", `line` + if not line: + return + for tag in self.tagdefs.keys(): + self.tag_remove(tag, mark, next) + chars = chars + line + m = self.prog.search(chars) + while m: + for key, value in m.groupdict().items(): + if value: + a, b = m.span(key) + self.tag_add(key, + head + "+%dc" % a, + head + "+%dc" % b) + if value in ("def", "class"): + m1 = self.idprog.match(chars, b) + if m1: + a, b = m1.span(1) + self.tag_add("DEFINITION", + head + "+%dc" % a, + head + "+%dc" % b) + m = self.prog.search(chars, m.end()) + if "SYNC" in self.tag_names(next + "-1c"): + head = next + chars = "" + else: + ok = 0 + if not ok: + # We're in an inconsistent state, and the call to + # update may tell us to stop. It may also change + # the correct value for "next" (since this is a + # line.col string, not a true mark). So leave a + # crumb telling the next invocation to resume here + # in case update tells us to leave. + self.tag_add("TODO", next) + self.update() + if self.stop_colorizing: + if __debug__: print "colorizing stopped" + return + + +def main(): + from Percolator import Percolator + root = Tk() + root.wm_protocol("WM_DELETE_WINDOW", root.quit) + text = Text(background="white") + text.pack(expand=1, fill="both") + text.focus_set() + p = Percolator(text) + d = ColorDelegator() + p.insertfilter(d) + root.mainloop() + +if __name__ == "__main__": + main() diff --git a/Lib/idlelib/ConfigParser.py b/Lib/idlelib/ConfigParser.py new file mode 100644 index 0000000..e1ce9dd --- /dev/null +++ b/Lib/idlelib/ConfigParser.py @@ -0,0 +1,382 @@ +"""Configuration file parser. + +A setup file consists of sections, lead by a "[section]" header, +and followed by "name: value" entries, with continuations and such in +the style of RFC 822. + +The option values can contain format strings which refer to other values in +the same section, or values in a special [DEFAULT] section. + +For example: + + something: %(dir)s/whatever + +would resolve the "%(dir)s" to the value of dir. All reference +expansions are done late, on demand. + +Intrinsic defaults can be specified by passing them into the +ConfigParser constructor as a dictionary. + +class: + +ConfigParser -- responsible for for parsing a list of + configuration files, and managing the parsed database. + + methods: + + __init__(defaults=None) + create the parser and specify a dictionary of intrinsic defaults. The + keys must be strings, the values must be appropriate for %()s string + interpolation. Note that `__name__' is always an intrinsic default; + it's value is the section's name. + + sections() + return all the configuration section names, sans DEFAULT + + has_section(section) + return whether the given section exists + + options(section) + return list of configuration options for the named section + + has_option(section, option) + return whether the given section has the given option + + read(filenames) + read and parse the list of named configuration files, given by + name. A single filename is also allowed. Non-existing files + are ignored. + + readfp(fp, filename=None) + read and parse one configuration file, given as a file object. + The filename defaults to fp.name; it is only used in error + messages (if fp has no `name' attribute, the string `' is used). + + get(section, option, raw=0, vars=None) + return a string value for the named option. All % interpolations are + expanded in the return values, based on the defaults passed into the + constructor and the DEFAULT section. Additional substitutions may be + provided using the `vars' argument, which must be a dictionary whose + contents override any pre-existing defaults. + + getint(section, options) + like get(), but convert value to an integer + + getfloat(section, options) + like get(), but convert value to a float + + getboolean(section, options) + like get(), but convert value to a boolean (currently defined as 0 or + 1, only) +""" + +import sys +import string +import re + +DEFAULTSECT = "DEFAULT" + + + +# exception classes +class Error: + def __init__(self, msg=''): + self._msg = msg + def __repr__(self): + return self._msg + +class NoSectionError(Error): + def __init__(self, section): + Error.__init__(self, 'No section: %s' % section) + self.section = section + +class DuplicateSectionError(Error): + def __init__(self, section): + Error.__init__(self, "Section %s already exists" % section) + self.section = section + +class NoOptionError(Error): + def __init__(self, option, section): + Error.__init__(self, "No option `%s' in section: %s" % + (option, section)) + self.option = option + self.section = section + +class InterpolationError(Error): + def __init__(self, reference, option, section, rawval): + Error.__init__(self, + "Bad value substitution:\n" + "\tsection: [%s]\n" + "\toption : %s\n" + "\tkey : %s\n" + "\trawval : %s\n" + % (section, option, reference, rawval)) + self.reference = reference + self.option = option + self.section = section + +class MissingSectionHeaderError(Error): + def __init__(self, filename, lineno, line): + Error.__init__( + self, + 'File contains no section headers.\nfile: %s, line: %d\n%s' % + (filename, lineno, line)) + self.filename = filename + self.lineno = lineno + self.line = line + +class ParsingError(Error): + def __init__(self, filename): + Error.__init__(self, 'File contains parsing errors: %s' % filename) + self.filename = filename + self.errors = [] + + def append(self, lineno, line): + self.errors.append((lineno, line)) + self._msg = self._msg + '\n\t[line %2d]: %s' % (lineno, line) + + + +class ConfigParser: + def __init__(self, defaults=None): + self.__sections = {} + if defaults is None: + self.__defaults = {} + else: + self.__defaults = defaults + + def defaults(self): + return self.__defaults + + def sections(self): + """Return a list of section names, excluding [DEFAULT]""" + # self.__sections will never have [DEFAULT] in it + return self.__sections.keys() + + def add_section(self, section): + """Create a new section in the configuration. + + Raise DuplicateSectionError if a section by the specified name + already exists. + """ + if self.__sections.has_key(section): + raise DuplicateSectionError(section) + self.__sections[section] = {} + + def has_section(self, section): + """Indicate whether the named section is present in the configuration. + + The DEFAULT section is not acknowledged. + """ + return self.__sections.has_key(section) + + def options(self, section): + """Return a list of option names for the given section name.""" + try: + opts = self.__sections[section].copy() + except KeyError: + raise NoSectionError(section) + opts.update(self.__defaults) + return opts.keys() + + def has_option(self, section, option): + """Return whether the given section has the given option.""" + try: + opts = self.__sections[section] + except KeyError: + raise NoSectionError(section) + return opts.has_key(option) + + def read(self, filenames): + """Read and parse a filename or a list of filenames. + + Files that cannot be opened are silently ignored; this is + designed so that you can specify a list of potential + configuration file locations (e.g. current directory, user's + home directory, systemwide directory), and all existing + configuration files in the list will be read. A single + filename may also be given. + """ + if type(filenames) is type(''): + filenames = [filenames] + for filename in filenames: + try: + fp = open(filename) + except IOError: + continue + self.__read(fp, filename) + fp.close() + + def readfp(self, fp, filename=None): + """Like read() but the argument must be a file-like object. + + The `fp' argument must have a `readline' method. Optional + second argument is the `filename', which if not given, is + taken from fp.name. If fp has no `name' attribute, `' is + used. + + """ + if filename is None: + try: + filename = fp.name + except AttributeError: + filename = '' + self.__read(fp, filename) + + def get(self, section, option, raw=0, vars=None): + """Get an option value for a given section. + + All % interpolations are expanded in the return values, based on the + defaults passed into the constructor, unless the optional argument + `raw' is true. Additional substitutions may be provided using the + `vars' argument, which must be a dictionary whose contents overrides + any pre-existing defaults. + + The section DEFAULT is special. + """ + try: + sectdict = self.__sections[section].copy() + except KeyError: + if section == DEFAULTSECT: + sectdict = {} + else: + raise NoSectionError(section) + d = self.__defaults.copy() + d.update(sectdict) + # Update with the entry specific variables + if vars: + d.update(vars) + option = self.optionxform(option) + try: + rawval = d[option] + except KeyError: + raise NoOptionError(option, section) + # do the string interpolation + if raw: + return rawval + + value = rawval # Make it a pretty variable name + depth = 0 + while depth < 10: # Loop through this until it's done + depth = depth + 1 + if string.find(value, "%(") >= 0: + try: + value = value % d + except KeyError, key: + raise InterpolationError(key, option, section, rawval) + else: + return value + + def __get(self, section, conv, option): + return conv(self.get(section, option)) + + def getint(self, section, option): + return self.__get(section, string.atoi, option) + + def getfloat(self, section, option): + return self.__get(section, string.atof, option) + + def getboolean(self, section, option): + v = self.get(section, option) + val = string.atoi(v) + if val not in (0, 1): + raise ValueError, 'Not a boolean: %s' % v + return val + + def optionxform(self, optionstr): + return string.lower(optionstr) + + # + # Regular expressions for parsing section headers and options. Note a + # slight semantic change from the previous version, because of the use + # of \w, _ is allowed in section header names. + SECTCRE = re.compile( + r'\[' # [ + r'(?P
[-\w_.*,(){}]+)' # a lot of stuff found by IvL + r'\]' # ] + ) + OPTCRE = re.compile( + r'(?P