bpo-38862: IDLE Strip Trailing Whitespace fixes end newlines (GH-17366)

Extra newlines are removed at the end of non-shell files. If the file only has newlines after stripping other trailing whitespace, all are removed, as is done by patchcheck.py.
This commit is contained in:
Terry Jan Reedy 2019-11-24 16:29:29 -05:00 committed by GitHub
parent 6f03b236c1
commit 6bf644ec82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 68 additions and 40 deletions

View File

@ -199,7 +199,8 @@ Format Paragraph
Strip trailing whitespace Strip trailing whitespace
Remove trailing space and other whitespace characters after the last Remove trailing space and other whitespace characters after the last
non-whitespace character of a line by applying str.rstrip to each line, non-whitespace character of a line by applying str.rstrip to each line,
including lines within multiline strings. including lines within multiline strings. Except for Shell windows,
remove extra newlines at the end of the file.
.. index:: .. index::
single: Run script single: Run script

View File

@ -3,6 +3,9 @@ Released on 2020-10-05?
====================================== ======================================
bpo-38862: 'Strip Trailing Whitespace' on the Format menu removes extra
newlines at the end of non-shell files.
bpo-38636: Fix IDLE Format menu tab toggle and file indent width. These bpo-38636: Fix IDLE Format menu tab toggle and file indent width. These
functions (default shortcuts Alt-T and Alt-U) were mistakenly disabled functions (default shortcuts Alt-T and Alt-U) were mistakenly disabled
in 3.7.5 and 3.8.0. in 3.7.5 and 3.8.0.

View File

@ -408,6 +408,16 @@ class Rstrip: # 'Strip Trailing Whitespace" on "Format" menu.
if cut < raw: if cut < raw:
text.delete('%i.%i' % (cur, cut), '%i.end' % cur) text.delete('%i.%i' % (cur, cut), '%i.end' % cur)
if (text.get('end-2c') == '\n' # File ends with at least 1 newline;
and not hasattr(self.editwin, 'interp')): # & is not Shell.
# Delete extra user endlines.
while (text.index('end-1c') > '1.0' # Stop if file empty.
and text.get('end-3c') == '\n'):
text.delete('end-3c')
# Because tk indexes are slice indexes and never raise,
# a file with only newlines will be emptied.
# patchcheck.py does the same.
undo.undo_block_stop() undo.undo_block_stop()

View File

@ -4,7 +4,7 @@
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>IDLE &#8212; Python 3.9.0a0 documentation</title> <title>IDLE &#8212; Python 3.9.0a1 documentation</title>
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" /> <link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
@ -17,14 +17,14 @@
<script type="text/javascript" src="../_static/sidebar.js"></script> <script type="text/javascript" src="../_static/sidebar.js"></script>
<link rel="search" type="application/opensearchdescription+xml" <link rel="search" type="application/opensearchdescription+xml"
title="Search within Python 3.9.0a0 documentation" title="Search within Python 3.9.0a1 documentation"
href="../_static/opensearch.xml"/> href="../_static/opensearch.xml"/>
<link rel="author" title="About these documents" href="../about.html" /> <link rel="author" title="About these documents" href="../about.html" />
<link rel="index" title="Index" href="../genindex.html" /> <link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" /> <link rel="search" title="Search" href="../search.html" />
<link rel="copyright" title="Copyright" href="../copyright.html" /> <link rel="copyright" title="Copyright" href="../copyright.html" />
<link rel="next" title="Other Graphical User Interface Packages" href="othergui.html" /> <link rel="next" title="Other Graphical User Interface Packages" href="othergui.html" />
<link rel="prev" title="tkinter.scrolledtext — Scrolled Text Widget" href="tkinter.scrolledtext.html" /> <link rel="prev" title="tkinter.tix — Extension widgets for Tk" href="tkinter.tix.html" />
<link rel="canonical" href="https://docs.python.org/3/library/idle.html" /> <link rel="canonical" href="https://docs.python.org/3/library/idle.html" />
@ -62,7 +62,7 @@
<a href="othergui.html" title="Other Graphical User Interface Packages" <a href="othergui.html" title="Other Graphical User Interface Packages"
accesskey="N">next</a> |</li> accesskey="N">next</a> |</li>
<li class="right" > <li class="right" >
<a href="tkinter.scrolledtext.html" title="tkinter.scrolledtext — Scrolled Text Widget" <a href="tkinter.tix.html" title="tkinter.tix — Extension widgets for Tk"
accesskey="P">previous</a> |</li> accesskey="P">previous</a> |</li>
<li><img src="../_static/py.png" alt="" <li><img src="../_static/py.png" alt=""
@ -71,7 +71,7 @@
<li> <li>
<a href="../index.html">3.9.0a0 Documentation</a> &#187; <a href="../index.html">3.9.0a1 Documentation</a> &#187;
</li> </li>
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li> <li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li>
@ -240,7 +240,8 @@ paragraph will be formatted to less than N columns, where N defaults to 72.</p>
</dd> </dd>
<dt>Strip trailing whitespace</dt><dd><p>Remove trailing space and other whitespace characters after the last <dt>Strip trailing whitespace</dt><dd><p>Remove trailing space and other whitespace characters after the last
non-whitespace character of a line by applying str.rstrip to each line, non-whitespace character of a line by applying str.rstrip to each line,
including lines within multiline strings.</p> including lines within multiline strings. Except for Shell windows,
remove extra newlines at the end of the file.</p>
</dd> </dd>
</dl> </dl>
</div> </div>
@ -886,8 +887,8 @@ also used for testing.</p>
</ul> </ul>
<h4>Previous topic</h4> <h4>Previous topic</h4>
<p class="topless"><a href="tkinter.scrolledtext.html" <p class="topless"><a href="tkinter.tix.html"
title="previous chapter"><code class="xref py py-mod docutils literal notranslate"><span class="pre">tkinter.scrolledtext</span></code> — Scrolled Text Widget</a></p> title="previous chapter"><code class="xref py py-mod docutils literal notranslate"><span class="pre">tkinter.tix</span></code> — Extension widgets for Tk</a></p>
<h4>Next topic</h4> <h4>Next topic</h4>
<p class="topless"><a href="othergui.html" <p class="topless"><a href="othergui.html"
title="next chapter">Other Graphical User Interface Packages</a></p> title="next chapter">Other Graphical User Interface Packages</a></p>
@ -919,7 +920,7 @@ also used for testing.</p>
<a href="othergui.html" title="Other Graphical User Interface Packages" <a href="othergui.html" title="Other Graphical User Interface Packages"
>next</a> |</li> >next</a> |</li>
<li class="right" > <li class="right" >
<a href="tkinter.scrolledtext.html" title="tkinter.scrolledtext — Scrolled Text Widget" <a href="tkinter.tix.html" title="tkinter.tix — Extension widgets for Tk"
>previous</a> |</li> >previous</a> |</li>
<li><img src="../_static/py.png" alt="" <li><img src="../_static/py.png" alt=""
@ -928,7 +929,7 @@ also used for testing.</p>
<li> <li>
<a href="../index.html">3.9.0a0 Documentation</a> &#187; <a href="../index.html">3.9.0a1 Documentation</a> &#187;
</li> </li>
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li> <li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li>
@ -959,11 +960,11 @@ also used for testing.</p>
<br /> <br />
<br /> <br />
Last updated on Sep 01, 2019. Last updated on Nov 24, 2019.
<a href="https://docs.python.org/3/bugs.html">Found a bug</a>? <a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
<br /> <br />
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 2.1.2. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 2.1.1.
</div> </div>
</body> </body>

View File

@ -40,8 +40,9 @@ class Func:
class Editor: class Editor:
'''Minimally imitate editor.EditorWindow class. '''Minimally imitate editor.EditorWindow class.
''' '''
def __init__(self, flist=None, filename=None, key=None, root=None): def __init__(self, flist=None, filename=None, key=None, root=None,
self.text = Text() text=None): # Allow real Text with mock Editor.
self.text = text or Text()
self.undo = UndoDelegator() self.undo = UndoDelegator()
def get_selection_indices(self): def get_selection_indices(self):

View File

@ -611,37 +611,33 @@ class IndentsTest(unittest.TestCase):
class RstripTest(unittest.TestCase): class RstripTest(unittest.TestCase):
def test_rstrip_line(self): @classmethod
editor = MockEditor() def setUpClass(cls):
text = editor.text requires('gui')
do_rstrip = ft.Rstrip(editor).do_rstrip cls.root = Tk()
eq = self.assertEqual cls.root.withdraw()
cls.text = Text(cls.root)
cls.editor = MockEditor(text=cls.text)
cls.do_rstrip = ft.Rstrip(cls.editor).do_rstrip
do_rstrip() @classmethod
eq(text.get('1.0', 'insert'), '') def tearDownClass(cls):
text.insert('1.0', ' ') del cls.text, cls.do_rstrip, cls.editor
do_rstrip() cls.root.update_idletasks()
eq(text.get('1.0', 'insert'), '') cls.root.destroy()
text.insert('1.0', ' \n') del cls.root
do_rstrip()
eq(text.get('1.0', 'insert'), '\n')
def test_rstrip_multiple(self): def tearDown(self):
editor = MockEditor() self.text.delete('1.0', 'end-1c')
# Comment above, uncomment 3 below to test with real Editor & Text.
#from idlelib.editor import EditorWindow as Editor
#from tkinter import Tk
#editor = Editor(root=Tk())
text = editor.text
do_rstrip = ft.Rstrip(editor).do_rstrip
def test_rstrip_lines(self):
original = ( original = (
"Line with an ending tab \n" "Line with an ending tab \n"
"Line ending in 5 spaces \n" "Line ending in 5 spaces \n"
"Linewithnospaces\n" "Linewithnospaces\n"
" indented line\n" " indented line\n"
" indented line with trailing space \n" " indented line with trailing space \n"
" ") " \n")
stripped = ( stripped = (
"Line with an ending tab\n" "Line with an ending tab\n"
"Line ending in 5 spaces\n" "Line ending in 5 spaces\n"
@ -649,9 +645,23 @@ class RstripTest(unittest.TestCase):
" indented line\n" " indented line\n"
" indented line with trailing space\n") " indented line with trailing space\n")
text.insert('1.0', original) self.text.insert('1.0', original)
do_rstrip() self.do_rstrip()
self.assertEqual(text.get('1.0', 'insert'), stripped) self.assertEqual(self.text.get('1.0', 'insert'), stripped)
def test_rstrip_end(self):
text = self.text
for code in ('', '\n', '\n\n\n'):
with self.subTest(code=code):
text.insert('1.0', code)
self.do_rstrip()
self.assertEqual(text.get('1.0','end-1c'), '')
for code in ('a\n', 'a\n\n', 'a\n\n\n'):
with self.subTest(code=code):
text.delete('1.0', 'end-1c')
text.insert('1.0', code)
self.do_rstrip()
self.assertEqual(text.get('1.0','end-1c'), 'a\n')
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -0,0 +1,2 @@
'Strip Trailing Whitespace' on the Format menu removes extra newlines
at the end of non-shell files.