Skip to content

Commit 7123ea0

Browse files
authored
bpo-17535: IDLE editor line numbers (GH-14030)
1 parent 1ebee37 commit 7123ea0

18 files changed

+891
-80
lines changed

Doc/library/idle.rst

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,22 +290,31 @@ Options menu (Shell and Editor)
290290
Configure IDLE
291291
Open a configuration dialog and change preferences for the following:
292292
fonts, indentation, keybindings, text color themes, startup windows and
293-
size, additional help sources, and extensions. On macOS, open the
293+
size, additional help sources, and extensions. On macOS, open the
294294
configuration dialog by selecting Preferences in the application
295-
menu. For more, see
295+
menu. For more details, see
296296
:ref:`Setting preferences <preferences>` under Help and preferences.
297297

298+
Most configuration options apply to all windows or all future windows.
299+
The option items below only apply to the active window.
300+
298301
Show/Hide Code Context (Editor Window only)
299302
Open a pane at the top of the edit window which shows the block context
300303
of the code which has scrolled above the top of the window. See
301-
:ref:`Code Context <code-context>` in the Editing and Navigation section below.
304+
:ref:`Code Context <code-context>` in the Editing and Navigation section
305+
below.
306+
307+
Show/Hide Line Numbers (Editor Window only)
308+
Open a column to the left of the edit window which shows the number
309+
of each line of text. The default is off, which may be changed in the
310+
preferences (see :ref:`Setting preferences <preferences>`).
302311

303312
Zoom/Restore Height
304313
Toggles the window between normal size and maximum height. The initial size
305314
defaults to 40 lines by 80 chars unless changed on the General tab of the
306315
Configure IDLE dialog. The maximum height for a screen is determined by
307316
momentarily maximizing a window the first time one is zoomed on the screen.
308-
Changing screen settings may invalidate the saved height. This toogle has
317+
Changing screen settings may invalidate the saved height. This toggle has
309318
no effect when a window is maximized.
310319

311320
Window menu (Shell and Editor)

Doc/whatsnew/3.7.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,13 @@ by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.)
10171017

10181018
The changes above have been backported to 3.6 maintenance releases.
10191019

1020+
New in 3.7.5:
1021+
1022+
Add optional line numbers for IDLE editor windows. Windows
1023+
open without line numbers unless set otherwise in the General
1024+
tab of the configuration dialog.
1025+
(Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.)
1026+
10201027

10211028
importlib
10221029
---------

Doc/whatsnew/3.8.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,11 @@ for certain types of invalid or corrupt gzip files.
515515
idlelib and IDLE
516516
----------------
517517

518+
Add optional line numbers for IDLE editor windows. Windows
519+
open without line numbers unless set otherwise in the General
520+
tab of the configuration dialog.
521+
(Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.)
522+
518523
Output over N lines (50 by default) is squeezed down to a button.
519524
N can be changed in the PyShell section of the General page of the
520525
Settings dialog. Fewer, but possibly extra long, lines can be squeezed by

Lib/idlelib/codecontext.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from sys import maxsize as INFINITY
1414

1515
import tkinter
16-
from tkinter.constants import TOP, X, SUNKEN
16+
from tkinter.constants import NSEW, SUNKEN
1717

1818
from idlelib.config import idleConf
1919

@@ -67,6 +67,7 @@ def __init__(self, editwin):
6767

6868
def _reset(self):
6969
self.context = None
70+
self.cell00 = None
7071
self.t1 = None
7172
self.topvisible = 1
7273
self.info = [(0, -1, "", False)]
@@ -105,25 +106,37 @@ def toggle_code_context_event(self, event=None):
105106
padx = 0
106107
border = 0
107108
for widget in widgets:
108-
padx += widget.tk.getint(widget.pack_info()['padx'])
109+
info = (widget.grid_info()
110+
if widget is self.editwin.text
111+
else widget.pack_info())
112+
padx += widget.tk.getint(info['padx'])
109113
padx += widget.tk.getint(widget.cget('padx'))
110114
border += widget.tk.getint(widget.cget('border'))
111115
self.context = tkinter.Text(
112-
self.editwin.top, font=self.text['font'],
116+
self.editwin.text_frame,
113117
height=1,
114118
width=1, # Don't request more than we get.
119+
highlightthickness=0,
115120
padx=padx, border=border, relief=SUNKEN, state='disabled')
121+
self.update_font()
116122
self.update_highlight_colors()
117123
self.context.bind('<ButtonRelease-1>', self.jumptoline)
118124
# Get the current context and initiate the recurring update event.
119125
self.timer_event()
120-
# Pack the context widget before and above the text_frame widget,
121-
# thus ensuring that it will appear directly above text_frame.
122-
self.context.pack(side=TOP, fill=X, expand=False,
123-
before=self.editwin.text_frame)
126+
# Grid the context widget above the text widget.
127+
self.context.grid(row=0, column=1, sticky=NSEW)
128+
129+
line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(),
130+
'linenumber')
131+
self.cell00 = tkinter.Frame(self.editwin.text_frame,
132+
bg=line_number_colors['background'])
133+
self.cell00.grid(row=0, column=0, sticky=NSEW)
124134
menu_status = 'Hide'
125135
else:
126136
self.context.destroy()
137+
self.context = None
138+
self.cell00.destroy()
139+
self.cell00 = None
127140
self.text.after_cancel(self.t1)
128141
self._reset()
129142
menu_status = 'Show'
@@ -221,8 +234,9 @@ def timer_event(self):
221234
self.update_code_context()
222235
self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event)
223236

224-
def update_font(self, font):
237+
def update_font(self):
225238
if self.context is not None:
239+
font = idleConf.GetFont(self.text, 'main', 'EditorWindow')
226240
self.context['font'] = font
227241

228242
def update_highlight_colors(self):
@@ -231,6 +245,11 @@ def update_highlight_colors(self):
231245
self.context['background'] = colors['background']
232246
self.context['foreground'] = colors['foreground']
233247

248+
if self.cell00 is not None:
249+
line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(),
250+
'linenumber')
251+
self.cell00.config(bg=line_number_colors['background'])
252+
234253

235254
CodeContext.reload()
236255

Lib/idlelib/config-highlight.def

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ hit-foreground= #ffffff
2222
hit-background= #000000
2323
error-foreground= #000000
2424
error-background= #ff7777
25+
context-foreground= #000000
26+
context-background= lightgray
27+
linenumber-foreground= gray
28+
linenumber-background= #ffffff
2529
#cursor (only foreground can be set, restart IDLE)
2630
cursor-foreground= black
2731
#shell window
@@ -31,8 +35,6 @@ stderr-foreground= red
3135
stderr-background= #ffffff
3236
console-foreground= #770000
3337
console-background= #ffffff
34-
context-foreground= #000000
35-
context-background= lightgray
3638

3739
[IDLE New]
3840
normal-foreground= #000000
@@ -55,6 +57,10 @@ hit-foreground= #ffffff
5557
hit-background= #000000
5658
error-foreground= #000000
5759
error-background= #ff7777
60+
context-foreground= #000000
61+
context-background= lightgray
62+
linenumber-foreground= gray
63+
linenumber-background= #ffffff
5864
#cursor (only foreground can be set, restart IDLE)
5965
cursor-foreground= black
6066
#shell window
@@ -64,8 +70,6 @@ stderr-foreground= red
6470
stderr-background= #ffffff
6571
console-foreground= #770000
6672
console-background= #ffffff
67-
context-foreground= #000000
68-
context-background= lightgray
6973

7074
[IDLE Dark]
7175
comment-foreground = #dd0000
@@ -97,3 +101,5 @@ comment-background = #002240
97101
break-foreground = #FFFFFF
98102
context-foreground= #ffffff
99103
context-background= #454545
104+
linenumber-foreground= gray
105+
linenumber-background= #002240

Lib/idlelib/config-main.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
# Additional help sources are listed in the [HelpFiles] section below
3737
# and should be viewable by a web browser (or the Windows Help viewer in
3838
# the case of .chm files). These sources will be listed on the Help
39-
# menu. The pattern, and two examples, are
39+
# menu. The pattern, and two examples, are:
4040
#
4141
# <sequence_number = menu item;/path/to/help/source>
4242
# 1 = IDLE;C:/Programs/Python36/Lib/idlelib/help.html
@@ -46,7 +46,7 @@
4646
# platform specific because of path separators, drive specs etc.
4747
#
4848
# The default files should not be edited except to add new sections to
49-
# config-extensions.def for added extensions . The user files should be
49+
# config-extensions.def for added extensions. The user files should be
5050
# modified through the Settings dialog.
5151

5252
[General]
@@ -65,6 +65,7 @@ font= TkFixedFont
6565
font-size= 10
6666
font-bold= 0
6767
encoding= none
68+
line-numbers-default= 0
6869

6970
[PyShell]
7071
auto-squeeze-min-lines= 50

Lib/idlelib/config.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ def GetThemeDict(self, type, themeName):
319319
'hit-background':'#000000',
320320
'error-foreground':'#ffffff',
321321
'error-background':'#000000',
322+
'context-foreground':'#000000',
323+
'context-background':'#ffffff',
324+
'linenumber-foreground':'#000000',
325+
'linenumber-background':'#ffffff',
322326
#cursor (only foreground can be set)
323327
'cursor-foreground':'#000000',
324328
#shell window
@@ -328,11 +332,11 @@ def GetThemeDict(self, type, themeName):
328332
'stderr-background':'#ffffff',
329333
'console-foreground':'#000000',
330334
'console-background':'#ffffff',
331-
'context-foreground':'#000000',
332-
'context-background':'#ffffff',
333335
}
334336
for element in theme:
335-
if not cfgParser.has_option(themeName, element):
337+
if not (cfgParser.has_option(themeName, element) or
338+
# Skip warning for new elements.
339+
element.startswith(('context-', 'linenumber-'))):
336340
# Print warning that will return a default color
337341
warning = ('\n Warning: config.IdleConf.GetThemeDict'
338342
' -\n problem retrieving theme element %r'

Lib/idlelib/configdialog.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,7 @@ def create_page_highlight(self):
819819
'Shell Error Text': ('error', '12'),
820820
'Shell Stdout Text': ('stdout', '13'),
821821
'Shell Stderr Text': ('stderr', '14'),
822+
'Line Number': ('linenumber', '16'),
822823
}
823824
self.builtin_name = tracers.add(
824825
StringVar(self), self.var_changed_builtin_name)
@@ -866,6 +867,11 @@ def create_page_highlight(self):
866867
('stderr', 'stderr'), ('\n\n', 'normal'))
867868
for texttag in text_and_tags:
868869
text.insert(END, texttag[0], texttag[1])
870+
n_lines = len(text.get('1.0', END).splitlines())
871+
for lineno in range(1, n_lines + 1):
872+
text.insert(f'{lineno}.0',
873+
f'{lineno:{len(str(n_lines))}d} ',
874+
'linenumber')
869875
for element in self.theme_elements:
870876
def tem(event, elem=element):
871877
# event.widget.winfo_top_level().highlight_target.set(elem)
@@ -1827,6 +1833,9 @@ def create_page_general(self):
18271833
frame_format: Frame
18281834
format_width_title: Label
18291835
(*)format_width_int: Entry - format_width
1836+
frame_line_numbers_default: Frame
1837+
line_numbers_default_title: Label
1838+
(*)line_numbers_default_bool: Checkbutton - line_numbers_default
18301839
frame_context: Frame
18311840
context_title: Label
18321841
(*)context_int: Entry - context_lines
@@ -1866,6 +1875,9 @@ def create_page_general(self):
18661875
IntVar(self), ('main', 'General', 'autosave'))
18671876
self.format_width = tracers.add(
18681877
StringVar(self), ('extensions', 'FormatParagraph', 'max-width'))
1878+
self.line_numbers_default = tracers.add(
1879+
BooleanVar(self),
1880+
('main', 'EditorWindow', 'line-numbers-default'))
18691881
self.context_lines = tracers.add(
18701882
StringVar(self), ('extensions', 'CodeContext', 'maxlines'))
18711883

@@ -1944,6 +1956,14 @@ def create_page_general(self):
19441956
validatecommand=self.digits_only, validate='key',
19451957
)
19461958

1959+
frame_line_numbers_default = Frame(frame_editor, borderwidth=0)
1960+
line_numbers_default_title = Label(
1961+
frame_line_numbers_default, text='Show line numbers in new windows')
1962+
self.line_numbers_default_bool = Checkbutton(
1963+
frame_line_numbers_default,
1964+
variable=self.line_numbers_default,
1965+
width=1)
1966+
19471967
frame_context = Frame(frame_editor, borderwidth=0)
19481968
context_title = Label(frame_context, text='Max Context Lines :')
19491969
self.context_int = Entry(
@@ -2021,6 +2041,10 @@ def create_page_general(self):
20212041
frame_format.pack(side=TOP, padx=5, pady=0, fill=X)
20222042
format_width_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
20232043
self.format_width_int.pack(side=TOP, padx=10, pady=5)
2044+
# frame_line_numbers_default.
2045+
frame_line_numbers_default.pack(side=TOP, padx=5, pady=0, fill=X)
2046+
line_numbers_default_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
2047+
self.line_numbers_default_bool.pack(side=LEFT, padx=5, pady=5)
20242048
# frame_context.
20252049
frame_context.pack(side=TOP, padx=5, pady=0, fill=X)
20262050
context_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
@@ -2063,6 +2087,8 @@ def load_general_cfg(self):
20632087
'main', 'General', 'autosave', default=0, type='bool'))
20642088
self.format_width.set(idleConf.GetOption(
20652089
'extensions', 'FormatParagraph', 'max-width', type='int'))
2090+
self.line_numbers_default.set(idleConf.GetOption(
2091+
'main', 'EditorWindow', 'line-numbers-default', type='bool'))
20662092
self.context_lines.set(idleConf.GetOption(
20672093
'extensions', 'CodeContext', 'maxlines', type='int'))
20682094

0 commit comments

Comments
 (0)