Skip to content

Commit 379033e

Browse files
Add a token browser extension to IDLE
The TokenBrowser extension adds a Token Browser command to the Tools menu. It opens a window listing the Python tokens of the editor content, or of the selection if there is one, with the token type names colored as by "python -m tokenize". There is one browser per editor; invoking the command again refreshes it and selects the token at the cursor. Selecting rows highlights the matching regions in the editor and, while the browser has focus, moves the editor cursor there; selecting text or moving the cursor in the editor selects the matching rows. Double-clicking a row (or pressing Escape) hides the browser, revealing the editor at the token. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a50b089 commit 379033e

13 files changed

Lines changed: 707 additions & 8 deletions

Doc/library/idle.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,21 @@ Stack Viewer
295295
Auto-open Stack Viewer
296296
Toggle automatically opening the stack viewer on an unhandled exception.
297297

298+
Tools menu (Shell and Editor)
299+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
300+
301+
Token Browser
302+
Open a window listing the Python tokens of the editor content
303+
(or, in the Shell, the current input),
304+
or of the selection if there is one.
305+
Token type names are colored as by ``python -m tokenize``.
306+
Selecting rows highlights the matching regions in the editor
307+
and moves the cursor there;
308+
selecting text or moving the cursor in the editor
309+
selects the matching rows.
310+
Double-click a row, or press :kbd:`Escape`,
311+
to hide the browser and return to the editor at the token.
312+
298313
Options menu (Shell and Editor)
299314
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
300315

Lib/idlelib/News3.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Released on 2026-10-01
44
=========================
55

66

7+
gh-152941: Add a Token Browser to IDLE, opened from the new Tools menu.
8+
It lists the Python tokens of the editor content, the Shell input, or
9+
the selection, with token type names colored as by `python -m tokenize`.
10+
Patch by Serhiy Storchaka and Claude Code.
11+
712
gh-152745: When "Run... Customized" with "Restart shell" unchecked
813
while Shell is running code, including waiting for an input('prompt:')
914
response, just report that the shell is executing instead of

Lib/idlelib/config-extensions.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ bell= True
4949
# See config-keys.def for notes on specifying keys and extend.txt for
5050
# information on creating IDLE extensions.
5151

52+
# A browser for the Python tokens of the editor, opened from the Tools menu.
53+
[TokenBrowser]
54+
enable= 1
55+
[TokenBrowser_cfgBindings]
56+
token-browser=
57+
5258
# A fake extension for testing and example purposes. When enabled and
5359
# invoked, inserts or deletes z-text at beginning of every line.
5460
[ZzDummy]

Lib/idlelib/editor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ def set_line_and_column(self, event=None):
425425
("edit", "_Edit"),
426426
("format", "F_ormat"),
427427
("run", "_Run"),
428+
("tools", "_Tools"),
428429
("options", "_Options"),
429430
("window", "_Window"),
430431
("help", "_Help"),
@@ -1153,6 +1154,7 @@ def get_standard_extension_names(self):
11531154
return idleConf.GetExtensions(editor_only=True)
11541155

11551156
extfiles = { # Map built-in config-extension section names to file names.
1157+
'TokenBrowser': 'tokenbrowser',
11561158
'ZzDummy': 'zzdummy',
11571159
}
11581160

Lib/idlelib/idle_test/htest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@
7979
"Verify x.y.z versions and test each button, including Close.\n "
8080
}
8181

82+
_token_browser_spec = {
83+
'file': 'tokenbrowser',
84+
'kwds': {},
85+
'msg': "Select rows in the token table and verify the matching regions\n"
86+
"are highlighted in the sample editor above. Select the whole\n"
87+
"editor text, or part of it, and press Refresh.\n"
88+
"Double-click a row and verify the editor cursor jumps to the\n"
89+
"start of that token and the editor gets focus."
90+
}
91+
8292
# TODO implement ^\; adding '<Control-Key-\\>' to function does not work.
8393
_calltip_window_spec = {
8494
'file': 'calltip_w',

Lib/idlelib/idle_test/test_config.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -420,11 +420,12 @@ def test_get_extensions(self):
420420
''')
421421
eq = self.assertEqual
422422
iGE = idleConf.GetExtensions
423-
eq(iGE(shell_only=True), [])
424-
eq(iGE(), ['ZzDummy'])
425-
eq(iGE(editor_only=True), ['ZzDummy'])
426-
eq(iGE(active_only=False), ['ZzDummy', 'DISABLE'])
427-
eq(iGE(active_only=False, editor_only=True), ['ZzDummy', 'DISABLE'])
423+
eq(iGE(shell_only=True), ['TokenBrowser'])
424+
eq(iGE(), ['TokenBrowser', 'ZzDummy'])
425+
eq(iGE(editor_only=True), ['TokenBrowser', 'ZzDummy'])
426+
eq(iGE(active_only=False), ['TokenBrowser', 'ZzDummy', 'DISABLE'])
427+
eq(iGE(active_only=False, editor_only=True),
428+
['TokenBrowser', 'ZzDummy', 'DISABLE'])
428429
userextn.remove_section('ZzDummy')
429430
userextn.remove_section('DISABLE')
430431

@@ -434,7 +435,8 @@ def test_remove_key_bind_names(self):
434435

435436
self.assertCountEqual(
436437
conf.RemoveKeyBindNames(conf.GetSectionList('default', 'extensions')),
437-
['AutoComplete', 'CodeContext', 'FormatParagraph', 'ParenMatch', 'ZzDummy'])
438+
['AutoComplete', 'CodeContext', 'FormatParagraph', 'ParenMatch',
439+
'TokenBrowser', 'ZzDummy'])
438440

439441
def test_get_extn_name_for_event(self):
440442
userextn.read_string('''

Lib/idlelib/idle_test/test_mainmenu.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class MainMenuTest(unittest.TestCase):
1111
def test_menudefs(self):
1212
actual = [item[0] for item in mainmenu.menudefs]
1313
expect = ['file', 'edit', 'format', 'run', 'shell',
14-
'debug', 'options', 'window', 'help']
14+
'debug', 'tools', 'options', 'window', 'help']
1515
self.assertEqual(actual, expect)
1616

1717
def test_default_keydefs(self):

0 commit comments

Comments
 (0)