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: 7 additions & 10 deletions Lib/idlelib/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
self.askinteger = tkSimpleDialog.askinteger
self.showerror = tkMessageBox.showerror

# Navigate bar
self.navigatebar = None

def _filename_to_unicode(self, filename):
"""Return filename as BMP unicode so diplayable in Tk."""
# Decode bytes to unicode.
Expand Down Expand Up @@ -565,16 +568,10 @@ def replace_event(self, event):
return "break"

def goto_line_event(self, event):
text = self.text
lineno = tkSimpleDialog.askinteger("Goto",
"Go to line number:",parent=text)
if lineno is None:
return "break"
if lineno <= 0:
text.bell()
return "break"
text.mark_set("insert", "%d.0" % lineno)
text.see("insert")
from idlelib.navigatebar import NavigateBar
if not self.navigatebar:
self.navigatebar = NavigateBar(self, self.text)
self.navigatebar.set_goto_line_mode()

def open_module(self, event=None):
"""Get module name from user and open it.
Expand Down
157 changes: 157 additions & 0 deletions Lib/idlelib/navigatebar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""Navigate bar
"""

from tkinter import Listbox, Scrollbar, X, Y, END, VERTICAL, LEFT, RIGHT
from tkinter.ttk import Frame, Entry, Label


class NavigateBar(Frame):
"""Navigate bar for IDLE.
"""

def __init__(self, editor, parent):
super().__init__(parent, borderwidth=5)
self.editor = editor
self.parent = parent # Should be text
self.create_widgets()
self.update_idletasks()
self.place(x=parent.winfo_width() / 2 - self.winfo_reqwidth() / 2, y=-2)
self.selection = 0
self.symbol_line = {}
self.mode = ''

def create_widgets(self):
self.bind('<FocusOut>', self.cancel)
self.parent.bind('<Configure>', self.winconfig_event)
self.label = Label(self, text='navigatebar')
self.label.pack(side=LEFT)
self.entry = Entry(self, width=50)
self.entry.bind('<KeyRelease-Up>', self.key_up_event)
self.entry.bind('<KeyRelease-Down>', self.key_down_event)
self.entry.bind('<KeyRelease>', self.navigate)
self.entry.bind('<KeyRelease-Return>', self.ok)
self.entry.focus_set()
self.entry.pack()

self.scrollbar = Scrollbar(self, orient=VERTICAL)
self.listbox = Listbox(self, yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.listbox.yview)
self.listbox.bind('<Button-1>', self.listselect_event)
self.listbox.bind('<Up>', self.key_up_event)
self.listbox.bind('<Down>', self.key_down_event)

self.entry.bind('<Escape>', self.cancel)
self.parent.bind('<Escape>', self.cancel)

def get_entry(self):
entry = self.entry.get().strip()
if not entry:
return ''
return entry

def ok(self, event=None):
if self.listbox.size():
self.goto_selection()
self.clear_highlight()
self.editor.navigatebar = None
self.parent.focus_set()
self.destroy()

def cancel(self, event=None):
self.clear_highlight()
self.editor.navigatebar = None
self.parent.focus_set()
self.destroy()

def winconfig_event(self, event):
self.update_idletasks()
self.place(x=self.parent.winfo_width() / 2 - self.winfo_reqwidth() / 2, y=-2)

def listselect_event(self, event=None):
self.goto_selection()

def key_up_event(self, event=None):
if self.selection > 0:
self.listbox.selection_clear(self.selection)
self.selection -= 1
self.listbox.select_set(self.selection)
self.listbox.see(self.selection)

self.goto_selection()

def key_down_event(self, event=None):
if self.selection < self.listbox.size() - 1:
self.listbox.selection_clear(self.selection)
self.selection += 1
self.listbox.select_set(self.selection)
self.listbox.see(self.selection)

self.goto_selection()

def set_goto_line_mode(self):
self.mode = 'goto'
self.label.config(text='Goto line: ')

def set_goto_symbol_mode(self):
self.mode = 'symbol'
self.label.config(text='Find symbol: ')

def goto_selection(self, event=None):
if self.listbox.size():
select = self.listbox.selection_get()
self.goto_lineno(self.symbol_line[select])

def goto_lineno(self, lineno):
self.clear_highlight()
self.parent.mark_set('insert', '%d.0' % (lineno))
self.parent.see('insert')
self.editor.set_line_and_column()

# Add highlight to target lineno
self.parent.tag_add('BREAK', '%d.0' % lineno, '%d.0' % (lineno + 1))

def navigate(self, event=None):
self.clear_listbox()
self.clear_highlight()

entry = self.get_entry()
if self.mode == 'goto':
try:
lineno = int(entry)
except ValueError:
return
self.goto_lineno(lineno)
elif self.mode == 'symbol':
self.update_symbol(entry)

def clear_listbox(self):
"""Show off listbox and clear listbox items
"""
self.scrollbar.pack_forget()
self.listbox.pack_forget()
self.listbox.delete(0, END)

def clear_highlight(self):
self.parent.tag_remove("BREAK", "insert linestart",
"insert lineend +1char")

def reset_listbox(self):
"""Show out listbox and reset selection
"""
self.scrollbar.pack(side=RIGHT, fill=Y)
self.listbox.pack(fill=X)
self.listbox.delete(0, END)
self.selection = 0

def update_symbol(self, entry):
self.reset_listbox()

texts = self.parent.get('1.0', 'end')
for index, text in enumerate(texts.split('\n')):
dedent_text = text.strip()
if dedent_text.startswith('class ') or dedent_text.startswith('def '):
if entry and not dedent_text.strip('class ').strip('def ').startswith(entry):
continue
self.listbox.insert(END, text)
self.symbol_line[text] = index + 1
self.listbox.select_set(self.selection)