M Debugger.py
M IOBinding.py M PyShell.py * Could not remove last set of saved breakpoints from a file * Starting with empty edit window, could not load a file * Multiple adjacent breakpoints were saved as one breakpoint * Storing breakpoints whenever a file is closed can get them out of synch with the saved version of a file. Only store them when the file is saved. * Add comment on current limitations on file editing in the presence of breakpoints. * Replace get_current_breaks() with update_breakpoints(), add an update to PyShellEditorWindow.breakpoints, which is the master breakpoint data structure, and which is used to reload the subprocess debugger. * Revert Debugger.Debugger.load_breakpoints() to use editwin.breakpoints. It is easier to debug the debugger if the breakpoint list in IDLE is identical to the list in the subprocess debugger and is transferred when the subprocess debugger is restarted, because this list can be easily queried. * Cleanup some linespacing and comments in IOBinding.py
This commit is contained in:
parent
3c05c1f01e
commit
bfed346259
|
@ -331,7 +331,7 @@ class Debugger:
|
||||||
for editwin in pyshell_edit_windows:
|
for editwin in pyshell_edit_windows:
|
||||||
filename = editwin.io.filename
|
filename = editwin.io.filename
|
||||||
try:
|
try:
|
||||||
for lineno in editwin.get_current_breaks():
|
for lineno in editwin.breakpoints:
|
||||||
self.set_breakpoint_here(filename, lineno)
|
self.set_breakpoint_here(filename, lineno)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -13,28 +13,9 @@ import tempfile
|
||||||
import tkFileDialog
|
import tkFileDialog
|
||||||
import tkMessageBox
|
import tkMessageBox
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from configHandler import idleConf
|
from configHandler import idleConf
|
||||||
|
|
||||||
#$ event <<open-window-from-file>>
|
|
||||||
#$ win <Control-o>
|
|
||||||
#$ unix <Control-x><Control-f>
|
|
||||||
|
|
||||||
#$ event <<save-window>>
|
|
||||||
#$ win <Control-s>
|
|
||||||
#$ unix <Control-x><Control-s>
|
|
||||||
|
|
||||||
#$ event <<save-window-as-file>>
|
|
||||||
#$ win <Alt-s>
|
|
||||||
#$ unix <Control-x><Control-w>
|
|
||||||
|
|
||||||
#$ event <<print-window>>
|
|
||||||
#$ win <Control-p>
|
|
||||||
#$ unix <Control-x><Control-p>
|
|
||||||
|
|
||||||
#$ event <<save-copy-of-window-as-file>>
|
|
||||||
#$ win <Alt-Shift-s>
|
|
||||||
#$ unix <Control-x><w>
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from codecs import BOM_UTF8
|
from codecs import BOM_UTF8
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -85,11 +66,12 @@ else:
|
||||||
encoding = encoding.lower()
|
encoding = encoding.lower()
|
||||||
|
|
||||||
coding_re = re.compile("coding[:=]\s*([-\w_.]+)")
|
coding_re = re.compile("coding[:=]\s*([-\w_.]+)")
|
||||||
|
|
||||||
def coding_spec(str):
|
def coding_spec(str):
|
||||||
|
|
||||||
"""Return the encoding declaration according to PEP 263.
|
"""Return the encoding declaration according to PEP 263.
|
||||||
Raise LookupError if the encoding is declared but unknown."""
|
|
||||||
|
|
||||||
|
Raise LookupError if the encoding is declared but unknown.
|
||||||
|
"""
|
||||||
# Only consider the first two lines
|
# Only consider the first two lines
|
||||||
str = str.split("\n")[:2]
|
str = str.split("\n")[:2]
|
||||||
str = "\n".join(str)
|
str = "\n".join(str)
|
||||||
|
@ -107,6 +89,7 @@ def coding_spec(str):
|
||||||
raise LookupError, "Unknown encoding "+name
|
raise LookupError, "Unknown encoding "+name
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
class IOBinding:
|
class IOBinding:
|
||||||
|
|
||||||
def __init__(self, editwin):
|
def __init__(self, editwin):
|
||||||
|
@ -218,14 +201,14 @@ class IOBinding:
|
||||||
self.set_filename(filename)
|
self.set_filename(filename)
|
||||||
self.text.mark_set("insert", "1.0")
|
self.text.mark_set("insert", "1.0")
|
||||||
self.text.see("insert")
|
self.text.see("insert")
|
||||||
|
|
||||||
self.updaterecentfileslist(filename)
|
self.updaterecentfileslist(filename)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def decode(self, chars):
|
def decode(self, chars):
|
||||||
# Try to create a Unicode string. If that fails, let Tcl try
|
"""Create a Unicode string
|
||||||
# its best
|
|
||||||
|
|
||||||
|
If that fails, let Tcl try its best
|
||||||
|
"""
|
||||||
# Check presence of a UTF-8 signature first
|
# Check presence of a UTF-8 signature first
|
||||||
if chars.startswith(BOM_UTF8):
|
if chars.startswith(BOM_UTF8):
|
||||||
try:
|
try:
|
||||||
|
@ -237,7 +220,6 @@ class IOBinding:
|
||||||
# Indicates that this file originally had a BOM
|
# Indicates that this file originally had a BOM
|
||||||
self.fileencoding = BOM_UTF8
|
self.fileencoding = BOM_UTF8
|
||||||
return chars
|
return chars
|
||||||
|
|
||||||
# Next look for coding specification
|
# Next look for coding specification
|
||||||
try:
|
try:
|
||||||
enc = coding_spec(chars)
|
enc = coding_spec(chars)
|
||||||
|
@ -248,19 +230,16 @@ class IOBinding:
|
||||||
"installation. The file may not display correctly" % name,
|
"installation. The file may not display correctly" % name,
|
||||||
master = self.text)
|
master = self.text)
|
||||||
enc = None
|
enc = None
|
||||||
|
|
||||||
if enc:
|
if enc:
|
||||||
try:
|
try:
|
||||||
return unicode(chars, enc)
|
return unicode(chars, enc)
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# If it is ASCII, we need not to record anything
|
# If it is ASCII, we need not to record anything
|
||||||
try:
|
try:
|
||||||
return unicode(chars, 'ascii')
|
return unicode(chars, 'ascii')
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Finally, try the locale's encoding. This is deprecated;
|
# Finally, try the locale's encoding. This is deprecated;
|
||||||
# the user should declare a non-ASCII encoding
|
# the user should declare a non-ASCII encoding
|
||||||
try:
|
try:
|
||||||
|
@ -295,8 +274,8 @@ class IOBinding:
|
||||||
else:
|
else:
|
||||||
if self.writefile(self.filename):
|
if self.writefile(self.filename):
|
||||||
self.set_saved(1)
|
self.set_saved(1)
|
||||||
|
self.editwin.store_file_breaks()
|
||||||
self.text.focus_set()
|
self.text.focus_set()
|
||||||
|
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
def save_as(self, event):
|
def save_as(self, event):
|
||||||
|
@ -305,8 +284,8 @@ class IOBinding:
|
||||||
if self.writefile(filename):
|
if self.writefile(filename):
|
||||||
self.set_filename(filename)
|
self.set_filename(filename)
|
||||||
self.set_saved(1)
|
self.set_saved(1)
|
||||||
|
self.editwin.store_file_breaks()
|
||||||
self.text.focus_set()
|
self.text.focus_set()
|
||||||
|
|
||||||
self.updaterecentfileslist(filename)
|
self.updaterecentfileslist(filename)
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
|
@ -315,7 +294,6 @@ class IOBinding:
|
||||||
if filename:
|
if filename:
|
||||||
self.writefile(filename)
|
self.writefile(filename)
|
||||||
self.text.focus_set()
|
self.text.focus_set()
|
||||||
|
|
||||||
self.updaterecentfileslist(filename)
|
self.updaterecentfileslist(filename)
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
|
@ -326,7 +304,6 @@ class IOBinding:
|
||||||
f = open(filename, "w")
|
f = open(filename, "w")
|
||||||
f.write(chars)
|
f.write(chars)
|
||||||
f.close()
|
f.close()
|
||||||
## print "saved to", `filename`
|
|
||||||
return True
|
return True
|
||||||
except IOError, msg:
|
except IOError, msg:
|
||||||
tkMessageBox.showerror("I/O Error", str(msg),
|
tkMessageBox.showerror("I/O Error", str(msg),
|
||||||
|
@ -338,14 +315,12 @@ class IOBinding:
|
||||||
# This is either plain ASCII, or Tk was returning mixed-encoding
|
# This is either plain ASCII, or Tk was returning mixed-encoding
|
||||||
# text to us. Don't try to guess further.
|
# text to us. Don't try to guess further.
|
||||||
return chars
|
return chars
|
||||||
|
|
||||||
# See whether there is anything non-ASCII in it.
|
# See whether there is anything non-ASCII in it.
|
||||||
# If not, no need to figure out the encoding.
|
# If not, no need to figure out the encoding.
|
||||||
try:
|
try:
|
||||||
return chars.encode('ascii')
|
return chars.encode('ascii')
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# If there is an encoding declared, try this first.
|
# If there is an encoding declared, try this first.
|
||||||
try:
|
try:
|
||||||
enc = coding_spec(chars)
|
enc = coding_spec(chars)
|
||||||
|
@ -358,17 +333,14 @@ class IOBinding:
|
||||||
return chars.encode(enc)
|
return chars.encode(enc)
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
failed = "Invalid encoding '%s'" % enc
|
failed = "Invalid encoding '%s'" % enc
|
||||||
|
|
||||||
if failed:
|
if failed:
|
||||||
tkMessageBox.showerror(
|
tkMessageBox.showerror(
|
||||||
"I/O Error",
|
"I/O Error",
|
||||||
"%s. Saving as UTF-8" % failed,
|
"%s. Saving as UTF-8" % failed,
|
||||||
master = self.text)
|
master = self.text)
|
||||||
|
|
||||||
# If there was a UTF-8 signature, use that. This should not fail
|
# If there was a UTF-8 signature, use that. This should not fail
|
||||||
if self.fileencoding == BOM_UTF8 or failed:
|
if self.fileencoding == BOM_UTF8 or failed:
|
||||||
return BOM_UTF8 + chars.encode("utf-8")
|
return BOM_UTF8 + chars.encode("utf-8")
|
||||||
|
|
||||||
# Try the original file encoding next, if any
|
# Try the original file encoding next, if any
|
||||||
if self.fileencoding:
|
if self.fileencoding:
|
||||||
try:
|
try:
|
||||||
|
@ -380,7 +352,6 @@ class IOBinding:
|
||||||
% self.fileencoding,
|
% self.fileencoding,
|
||||||
master = self.text)
|
master = self.text)
|
||||||
return BOM_UTF8 + chars.encode("utf-8")
|
return BOM_UTF8 + chars.encode("utf-8")
|
||||||
|
|
||||||
# Nothing was declared, and we had not determined an encoding
|
# Nothing was declared, and we had not determined an encoding
|
||||||
# on loading. Recommend an encoding line.
|
# on loading. Recommend an encoding line.
|
||||||
try:
|
try:
|
||||||
|
@ -469,11 +440,8 @@ class IOBinding:
|
||||||
filetypes=self.filetypes)
|
filetypes=self.filetypes)
|
||||||
return self.savedialog.show(initialdir=dir, initialfile=base)
|
return self.savedialog.show(initialdir=dir, initialfile=base)
|
||||||
|
|
||||||
|
|
||||||
def updaterecentfileslist(self,filename):
|
def updaterecentfileslist(self,filename):
|
||||||
#
|
"Update recent file list on all editor windows"
|
||||||
# Updates recent file list on all editor windows
|
|
||||||
#
|
|
||||||
self.editwin.UpdateRecentFilesList(filename)
|
self.editwin.UpdateRecentFilesList(filename)
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
|
|
|
@ -67,10 +67,6 @@ linecache.checkcache = extended_linecache_checkcache
|
||||||
class PyShellEditorWindow(EditorWindow):
|
class PyShellEditorWindow(EditorWindow):
|
||||||
"Regular text edit window when a shell is present"
|
"Regular text edit window when a shell is present"
|
||||||
|
|
||||||
# XXX KBK 10Dec02 Breakpoints are currently removed if module is modified.
|
|
||||||
# In the future, it may be possible to preserve breakpoints by changing
|
|
||||||
# their line numbers as a module is modified.
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
self.breakpoints = []
|
self.breakpoints = []
|
||||||
apply(EditorWindow.__init__, (self,) + args)
|
apply(EditorWindow.__init__, (self,) + args)
|
||||||
|
@ -78,11 +74,12 @@ class PyShellEditorWindow(EditorWindow):
|
||||||
self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
|
self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
|
||||||
self.text.bind("<<open-python-shell>>", self.flist.open_shell)
|
self.text.bind("<<open-python-shell>>", self.flist.open_shell)
|
||||||
|
|
||||||
self.breakpointPath=os.path.join(idleConf.GetUserCfgDir(), 'breakpoints.lst')
|
self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(),
|
||||||
|
'breakpoints.lst')
|
||||||
# whenever a file is changed, restore breakpoints
|
# whenever a file is changed, restore breakpoints
|
||||||
if self.io.filename: self.restore_file_breaks()
|
if self.io.filename: self.restore_file_breaks()
|
||||||
def filename_changed_hook(old_hook=self.io.filename_change_hook,self=self):
|
def filename_changed_hook(old_hook=self.io.filename_change_hook,
|
||||||
|
self=self):
|
||||||
self.restore_file_breaks()
|
self.restore_file_breaks()
|
||||||
old_hook()
|
old_hook()
|
||||||
self.io.set_filename_change_hook(filename_changed_hook)
|
self.io.set_filename_change_hook(filename_changed_hook)
|
||||||
|
@ -148,49 +145,82 @@ class PyShellEditorWindow(EditorWindow):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def store_file_breaks(self):
|
def store_file_breaks(self):
|
||||||
if not self.breakpoints:
|
"Save breakpoints when file is saved"
|
||||||
return
|
# XXX 13 Dec 2002 KBK Currently the file must be saved before it can
|
||||||
filename=self.io.filename
|
# be run. The breaks are saved at that time. If we introduce
|
||||||
|
# a temporary file save feature the save breaks functionality
|
||||||
|
# needs to be re-verified, since the breaks at the time the
|
||||||
|
# temp file is created may differ from the breaks at the last
|
||||||
|
# permanent save of the file. A break introduced after a save
|
||||||
|
# will be effective, but not persistent. This is necessary to
|
||||||
|
# keep the saved breaks synched with the saved file.
|
||||||
|
#
|
||||||
|
# Breakpoints are set as tagged ranges in the text. Certain
|
||||||
|
# kinds of edits cause these ranges to be deleted: Inserting
|
||||||
|
# or deleting a line just before a breakpoint, and certain
|
||||||
|
# deletions prior to a breakpoint. These issues need to be
|
||||||
|
# investigated and understood. It's not clear if they are
|
||||||
|
# Tk issues or IDLE issues, or whether they can actually
|
||||||
|
# be fixed. Since a modified file has to be saved before it is
|
||||||
|
# run, and since self.breakpoints (from which the subprocess
|
||||||
|
# debugger is loaded) is updated during the save, the visible
|
||||||
|
# breaks stay synched with the subprocess even if one of these
|
||||||
|
# unexpected breakpoint deletions occurs.
|
||||||
|
breaks = self.breakpoints
|
||||||
|
filename = self.io.filename
|
||||||
try:
|
try:
|
||||||
lines=open(self.breakpointPath,"r").readlines()
|
lines = open(self.breakpointPath,"r").readlines()
|
||||||
except IOError:
|
except IOError:
|
||||||
lines=[]
|
lines = []
|
||||||
new_file=open(self.breakpointPath,"w")
|
new_file = open(self.breakpointPath,"w")
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if not line.startswith(filename+"="):
|
if not line.startswith(filename + '='):
|
||||||
new_file.write(line)
|
new_file.write(line)
|
||||||
new_file.write(filename+"="+`self.get_current_breaks()`+"\n")
|
self.update_breakpoints()
|
||||||
|
breaks = self.breakpoints
|
||||||
|
if breaks:
|
||||||
|
new_file.write(filename + '=' + str(breaks) + '\n')
|
||||||
new_file.close()
|
new_file.close()
|
||||||
|
|
||||||
def restore_file_breaks(self):
|
def restore_file_breaks(self):
|
||||||
self.text.update() # this enables setting "BREAK" tags to be visible
|
self.text.update() # this enables setting "BREAK" tags to be visible
|
||||||
filename=self.io.filename
|
filename = self.io.filename
|
||||||
|
if filename is None:
|
||||||
|
return
|
||||||
if os.path.isfile(self.breakpointPath):
|
if os.path.isfile(self.breakpointPath):
|
||||||
lines=open(self.breakpointPath,"r").readlines()
|
lines = open(self.breakpointPath,"r").readlines()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if line.startswith(filename+"="):
|
if line.startswith(filename + '='):
|
||||||
breakpoint_linenumbers=eval(line[len(filename)+1:])
|
breakpoint_linenumbers = eval(line[len(filename)+1:])
|
||||||
for breakpoint_linenumber in breakpoint_linenumbers:
|
for breakpoint_linenumber in breakpoint_linenumbers:
|
||||||
self.set_breakpoint(breakpoint_linenumber)
|
self.set_breakpoint(breakpoint_linenumber)
|
||||||
|
|
||||||
def get_current_breaks(self):
|
def update_breakpoints(self):
|
||||||
#
|
"Retrieves all the breakpoints in the current window"
|
||||||
# retrieves all the breakpoints in the current window
|
|
||||||
#
|
|
||||||
text = self.text
|
text = self.text
|
||||||
lines = text.tag_ranges("BREAK")
|
ranges = text.tag_ranges("BREAK")
|
||||||
result = [int(float((lines[i]))) for i in range(0,len(lines),2)]
|
linenumber_list = self.ranges_to_linenumbers(ranges)
|
||||||
return result
|
self.breakpoints = linenumber_list
|
||||||
|
|
||||||
def saved_change_hook(self):
|
def ranges_to_linenumbers(self, ranges):
|
||||||
"Extend base method - clear breaks if module is modified"
|
lines = []
|
||||||
if not self.get_saved():
|
for index in range(0, len(ranges), 2):
|
||||||
self.clear_file_breaks()
|
lineno = int(float(ranges[index]))
|
||||||
EditorWindow.saved_change_hook(self)
|
end = int(float(ranges[index+1]))
|
||||||
|
while lineno < end:
|
||||||
|
lines.append(lineno)
|
||||||
|
lineno += 1
|
||||||
|
return lines
|
||||||
|
|
||||||
|
# XXX 13 Dec 2020 KBK Not used currently
|
||||||
|
# def saved_change_hook(self):
|
||||||
|
# "Extend base method - clear breaks if module is modified"
|
||||||
|
# if not self.get_saved():
|
||||||
|
# self.clear_file_breaks()
|
||||||
|
# EditorWindow.saved_change_hook(self)
|
||||||
|
|
||||||
def _close(self):
|
def _close(self):
|
||||||
"Extend base method - clear breaks when module is closed"
|
"Extend base method - clear breaks when module is closed"
|
||||||
self.store_file_breaks()
|
|
||||||
self.clear_file_breaks()
|
self.clear_file_breaks()
|
||||||
EditorWindow._close(self)
|
EditorWindow._close(self)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue