diff -r ace52be8da89 Doc/library/idle.rst --- a/Doc/library/idle.rst Sat Mar 23 16:06:06 2013 -0700 +++ b/Doc/library/idle.rst Sun Mar 24 18:15:37 2013 -0400 @@ -235,6 +235,10 @@ Open a pane at the top of the edit window which shows the block context of the section of code which is scrolling off the top of the window. +Show Line Numbers (toggle)(Editor Window only) + Toggles the line number IDLE extension which displays line numbers on the + left hand side of the editor window. + Windows menu (Shell and Editor) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff -r ace52be8da89 Lib/idlelib/LineNumbers.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/idlelib/LineNumbers.py Sun Mar 24 18:15:37 2013 -0400 @@ -0,0 +1,353 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## LineNumbers Extension +## +## Provides line numbers to the left of the source code. +## +## The width of the line numbers adapts. Limit of 99,999 lines (for proper display). +## +## """ + +config_extension_def = """ + +[LineNumbers] +enable=1 +enable_shell=0 +visible=True + +[LineNumbers_cfgBindings] +linenumbers-show= + +""" + +import sys +if sys.version < '3': + from Tkinter import END, Text, LEFT, Y, NONE, RIGHT, NORMAL, DISABLED, Label, TOP, Frame, X +else: + from tkinter import END, Text, LEFT, Y, NONE, RIGHT, NORMAL, DISABLED, Label, TOP, Frame, X + +from idlelib.configHandler import idleConf +from idlelib.Delegator import Delegator +from idlelib.Percolator import Percolator + + +FONTUPDATEINTERVAL = 1000 # milliseconds + +_AFTER_UNDO = True # Flag to have the LineNumberDelegator inserted after the undo delegator + +jn = lambda x,y: '%i.%i' % (x,y) # join integers to text coordinates +sp = lambda x: map(int, x.split('.')) # convert tkinter Text coordinate to a line and column tuple + + +def dbprint(func): # A decorator for debugging + def f(*args, **kwargs): + print(func, args, kwargs) + return func(*args, **kwargs) + return f + +class LineNumbers(object): + + menudefs = [('options', [('!Show Line Numbers', '<>')])] + + def __init__(self, editwin): + self.editwin = editwin + self.text = self.editwin.text + self.textfont = None + self.width = 2 + self.after_id = None + + self.create_linenumbers() + + e = idleConf.GetOption("extensions", "LineNumbers", + "visible", type="bool", default=True) + self.set_visible(e) + + self.code_context_fix() + + def close(self): + if self.after_id: + self.text.after_cancel(self.after_id) + self.visible = False + + def adjust_font(self): + try: + # taken from CodeContext.py + newtextfont = self.editwin.text["font"] + if self.textln and newtextfont != self.textfont: + self.textfont = newtextfont + self.textln["font"] = self.textfont + if self._cc_text: + self._cc_text["font"] = self.textfont + self.update_numbers() + except Exception as err: + import traceback; traceback.print_exc() + + def font_timer(self): + if not self.visible: + return + + self.adjust_font() + + if self.after_id: + self.text.after_cancel(self.after_id) + self.after_id = self.text.after(FONTUPDATEINTERVAL, self.font_timer) + if not _AFTER_UNDO: + self.update_numbers() # fixes a bug due to this percolator being ahead of undo percolator. + + def set_visible(self, b=True): + self.visible = b + + if self.visible: + self.text.after(1, self.font_timer) # avoid a start-up bug + self.show() + # use .after to avoid a start-up error caused by update_idletasks in update_numbers + self.text.after(1, self.update_numbers) + else: + self.hide() + + idleConf.SetOption("extensions", "LineNumbers", + "visible", '%s' % self.visible) + + self.editwin.setvar("<>", self.visible) + + def linenumbers_show_event(self, ev=None): + self.set_visible(not self.visible) + self._code_context_toggle() + + def create_linenumbers(self): + """ Create the widget for displaying line numbers. """ + editwin = self.editwin + text = self.text + text_frame = editwin.text_frame + self.textln = textln = Text(text_frame, width=self.width, + height=1, wrap=NONE) + + # adjust font + textln.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'), + idleConf.GetOption('main', 'EditorWindow', 'font-size'))) + + textln.bind("", self.focus_in_event) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + + textln.bind("", self.button4) + textln.bind("", self.button5) + + textln.tag_config('LINE', justify=RIGHT) + textln.insert(END, '1') + textln.tag_add('LINE', '1.0', END) + + # start the line numbers + self.per = per = Percolator(textln) + self.line_delegator = LineDelegator() + per.insertfilter(self.line_delegator) + textln._insert = self.line_delegator.delegate.insert + textln._delete = self.line_delegator.delegate.delete + + + lines = LineNumberDelegator(self) + if _AFTER_UNDO: + # Percolator.py's .insertfilter should have an "after=" argument + lines.setdelegate(editwin.undo.delegate) + editwin.undo.setdelegate(lines) + else: + editwin.per.insertfilter(lines) + + editwin.vbar['command'] = self.vbar_split + editwin.text['yscrollcommand'] = self.yscroll_split + + def button4(self, ev=None): + self.text.event_generate("") + return "break" + + def button5(self, ev=None): + self.text.event_generate("") + return "break" + + def button_ignore(self, ev=None): + return "break" + + def show(self): + self.textln.pack(side=LEFT, fill=Y, before=self.editwin.text) + + def hide(self): + self.textln.pack_forget() + + def focus_in_event(self, event=None): + self.editwin.text.focus_set() + self.textln.tag_remove('sel', '1.0', 'end') + #self.editwin.text.event_generate("<>") + + def generate_goto_event(self, event=None): + self.editwin.text.event_generate("<>") + return "break" + + def vbar_split(self, *args, **kwargs): + """ split scrollbar commands to the editor text widget and the line number widget """ + self.textln.yview(*args, **kwargs) + self.text.yview(*args, **kwargs) + + def yscroll_split(self, *args, **kwargs): + """ send yview commands to both the scroll bar and line number listing """ + #import traceback; traceback.print_stack() + self.editwin.vbar.set(*args) + self.textln.yview_moveto(args[0]) + + def update_numbers(self, add=None, remove=None): + if not self.visible: return + + textln = self.textln + text = self.editwin.text + + + endline1, col1 = sp(text.index(END)) + endline2, col2 = sp(textln.index(END)) + + + if endline1 < endline2: + # delete numbers + textln._delete('%i.0' % endline1, END) + elif endline1 > endline2: + # add numbers + q = range(endline2, endline1) + r = map(lambda x: '%i' % x, q) + s = '\n' + '\n'.join(r) + textln._insert(END, s) + textln.tag_add('LINE', '1.0', END) + + # adjust width of textln, if needed. (counts from 1, not zero) + if endline1 <= 100: + width = 2 + elif endline1 <= 1000: + width = 3 + elif endline1 <= 10000: + width = 4 + else: + width = 5 # more than 9999 lines in IDLE? Really? + + # XXX: If your code requires width>5, i.e > 100,000 lines of code, + # you probably should not be using IDLE. + + if width > self.width: # 2011-12-18 - only grow, not shrink + self.width = width + textln.configure(width=width) + if self._cc_text: # adjust CC width + self._cc_text.configure(width=width) + + self.textln.update_idletasks() + a = self.text.yview() + self.textln.yview_moveto(a[0]) + + def code_context_fix(self): + self._cc_text = None + self._cc_frame = None + def f(): + self.text.bind('<>', self._code_context_toggle, '+') + self._code_context_toggle() + self.text.after(10, f) + + def _code_context_toggle(self, event=None): + cc = self.editwin.extensions.get('CodeContext', None) + if cc is None: + return + + if not self.visible: + if self._cc_frame: + L = cc.label + L.pack_forget() + self._cc_frame.destroy() + L.pack(side=TOP, fill=X, expand=False, + before=self.editwin.text_frame) + return + + + editwin = self.editwin + text = self.text + text_frame = editwin.text_frame + + # repack the Label in a frame + if cc.label: + cc.label.pack_forget() + F = Frame(self.editwin.top) + F.lower() # fix Z-order + t = Text(F, width=self.width, height=1, + takefocus=0) + t.bind("", lambda x: self.text.focus()) + t["font"] = self.textln.cget('font') + t.pack(side=LEFT, fill=Y) + cc.label.pack(in_=F, fill=X, expand=False) + + F.pack(side=TOP, before=text_frame, fill=X, expand=False) + self._cc_frame = F + self._cc_text = t + else: + if self._cc_frame: + self._cc_frame.destroy() + self._cc_frame = None + self._cc_text = None + + + + +class LineNumberDelegator(Delegator): + # for editwin.text + def __init__(self, line_number_instance): + Delegator.__init__(self) + self.ext = line_number_instance + + def insert(self, index, chars, tags=None): + self.delegate.insert(index, chars, tags) + if '\n' in chars: + self.ext.update_numbers()#add=chars.count('\n')) + def delete(self, index1, index2=None): + t = self.get(index1, index2) + self.delegate.delete(index1, index2) + if '\n' in t: + self.ext.update_numbers()#remove=t.count('\n')) + + +class LineDelegator(Delegator): + # for textln + def insert(self, *args, **kargs): + pass + + def delete(self, *args, **kargs): + pass diff -r ace52be8da89 Lib/idlelib/config-extensions.def --- a/Lib/idlelib/config-extensions.def Sat Mar 23 16:06:06 2013 -0700 +++ b/Lib/idlelib/config-extensions.def Sun Mar 24 18:15:37 2013 -0400 @@ -94,3 +94,10 @@ enable_shell=0 enable_editor=1 +[LineNumbers] +enable=1 +enable_shell=0 +visible=True + +[LineNumbers_cfgBindings] +linenumbers-show= diff -r ace52be8da89 Lib/idlelib/help.txt --- a/Lib/idlelib/help.txt Sat Mar 23 16:06:06 2013 -0700 +++ b/Lib/idlelib/help.txt Sun Mar 24 18:15:37 2013 -0400 @@ -132,11 +132,17 @@ sources can be specified. --- - Code Context (toggle) -- Open a pane at the top of the edit window - which shows the block context of the section - of code which is scrolling off the top or the - window. This is not present in the Shell - window only the Editor window. + Code Context (toggle) -- Open a pane at the top of the edit window + which shows the block context of the + section of code which is scrolling off + the top of the window. This is not + present in the Shell window only the + Editor window. + Show Line Numbers (toggle) -- Toggles the line number IDLE extension + which displays line numbers on the left + hand side of the editor window. This is + not present in the Shell window only the + Editor window. Windows Menu (Shell and Editor):