Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions Lib/idlelib/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from idlelib import replace
from idlelib import search
from idlelib import windows
from idlelib import sidebar

# The default tab setting for a Text widget, in average-width characters.
TK_TABWIDTH_DEFAULT = 8
Expand Down Expand Up @@ -57,7 +58,7 @@ class EditorWindow(object):
filesystemencoding = sys.getfilesystemencoding() # for file names
help_url = None

def __init__(self, flist=None, filename=None, key=None, root=None):
def __init__(self, flist=None, filename=None, key=None, root=None, has_lineno=False):
if EditorWindow.help_url is None:
dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html')
if sys.platform.count('linux'):
Expand Down Expand Up @@ -120,7 +121,8 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
'height': idleConf.GetOption(
'main', 'EditorWindow', 'height', type='int'),
}
self.text = text = MultiCallCreator(Text)(text_frame, **text_options)

self.text = text = MultiCallCreator(sidebar.SidebarText)(text_frame, **text_options)
self.top.focused_widget = self.text

self.createmenubar()
Expand Down Expand Up @@ -190,6 +192,14 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
text['yscrollcommand'] = vbar.set
text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow')
text_frame.pack(side=LEFT, fill=BOTH, expand=1)

if has_lineno:
self.sidebar = sidebar.LineNumberSidebar(text_frame, width=48)
self.sidebar.attach(text)
self.sidebar.pack(side=LEFT, fill=Y)
self.text.bind('<<Change>>', self.set_sidebar)
self.text.bind('<Configure>', self.set_sidebar)

text.pack(side=TOP, fill=BOTH, expand=1)
text.focus_set()

Expand Down Expand Up @@ -355,6 +365,9 @@ def set_line_and_column(self, event=None):
self.status_bar.set_label('column', 'Col: %s' % column)
self.status_bar.set_label('line', 'Ln: %s' % line)

def set_sidebar(self, event=None):
self.sidebar.redraw()

menu_specs = [
("file", "_File"),
("edit", "_Edit"),
Expand Down
2 changes: 1 addition & 1 deletion Lib/idlelib/pyshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class PyShellEditorWindow(EditorWindow):

def __init__(self, *args):
self.breakpoints = []
EditorWindow.__init__(self, *args)
EditorWindow.__init__(self, has_lineno=True, *args)
self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
self.text.bind("<<open-python-shell>>", self.flist.open_shell)
Expand Down
71 changes: 71 additions & 0 deletions Lib/idlelib/sidebar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'''Provide line number sidebar to editor

Inspire by Bryan Oakley, on stackflow: https://stackoverflow.com/a/16375233
'''

import tkinter as tk


class LineNumberSidebar(tk.Canvas):
def __init__(self, *args, **kwargs):
tk.Canvas.__init__(self, highlightthickness=0, background='grey93', *args, **kwargs)

def attach(self, text_widget):
self.text = text_widget

def redraw(self, *args):
'''redraw line numbers'''
self.delete('all')

# Get cursor lineno
curline, column = self.text.index(tk.INSERT).split('.')

# Redraw from top
i = self.text.index('@0,0')
while True:
dline = self.text.dlineinfo(i)
if dline is None:
break

lineno = i.split('.')[0]
text = self.create_text(45, dline[1], anchor='ne', text=lineno)

# Highlight cursor line number with white rectangle
if lineno == curline:
n, e, s, w = self.bbox(text)
r = self.create_rectangle((1, e, s + 3, w), fill='white', outline='white')
self.tag_lower(r, text)

# Update index
i = self.text.index('%s + 1line' % i)


class SidebarText(tk.Text):
def __init__(self, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)

self.tk.eval('''
proc widget_proxy {widget widget_command args} {

# call the real tk widget command with the real args
set result [uplevel [linsert $args 0 $widget_command]]

# generate the event for certain types of commands
if {([lindex $args 0] in {insert replace delete}) ||
([lrange $args 0 2] == {mark set insert}) ||
([lrange $args 0 1] == {xview moveto}) ||
([lrange $args 0 1] == {xview scroll}) ||
([lrange $args 0 1] == {yview moveto}) ||
([lrange $args 0 1] == {yview scroll})} {

event generate $widget <<Change>> -when tail
}

# return the result from the real widget command
return $result
}
''')
self.tk.eval('''
rename {widget} _{widget}
interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget}
'''.format(widget=str(self)))