<body><div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: #000000;text-align: left" dir="ltr">
                                        Hi Christoph,<div><br></div><div>why is this a bug? A quick experiment in Sublime Text suggested that this might be the expected behavior. At least for actually empty lines, having not a single white-space character.</div><div><br></div><div>Best,</div><div>Marcle</div><div class="mb_sig"></div><blockquote class='history_container' type='cite' style='border-left-style:solid;border-width:1px; margin-top:20px; margin-left:0px;padding-left:10px;'>
                        <p style='color: #AAAAAA; margin-top: 10px;'>Am 07.03.2021 20:43:09 schrieb commits@source.squeak.org <commits@source.squeak.org>:</p><div style='font-family:Arial,Helvetica,sans-serif'>Christoph Thiede uploaded a new version of Morphic to project The Inbox:<br>http://source.squeak.org/inbox/Morphic-ct.1733.mcz<br><br>==================== Summary ====================<br><br>Name: Morphic-ct.1733<br>Author: ct<br>Time: 7 March 2021, 8:42:45.180906 pm<br>UUID: b8246d49-b132-40af-8ad4-7428c544390c<br>Ancestors: Morphic-mt.1731<br><br>Fixes an eternity-old bug in TextEditor which skipped empty lines when indenting/outdenting the selection.<br><br>=============== Diff against Morphic-mt.1731 ===============<br><br>Item was changed:<br>  ----- Method: TextEditor>>inOutdent:delta: (in category 'editing keys') -----<br>  inOutdent: aKeyboardEvent delta: delta<br>   "Add/remove a tab at the front of every line occupied by the selection. Flushes typeahead.  Derived from work by Larry Tesler back in December 1985.  Now triggered by Cmd-L and Cmd-R.  2/29/96 sw"<br>  <br>    | realStart realStop lines startLine stopLine start stop adjustStart "indentation" numLines oldText newText newSize |<br>- <br>   "Operate on entire lines, but remember the real selection for re-highlighting later"<br>        realStart := self startIndex.<br>         realStop := self stopIndex - 1.<br>  <br>   "Special case a caret on a line of its own, including weird case at end of paragraph"<br>       (realStart > realStop and:<br>                                 [realStart < 2 or: [(paragraph string at: realStart - 1) == Character cr or: [(paragraph string at: realStart - 1) == Character lf]]])<br>             ifTrue:<br>                       [delta < 0<br>                                 ifTrue:<br>                                       [morph flash]<br>                                 ifFalse:<br>                                      [self replaceSelectionWith: Character tab asText.<br>                                     self selectAt: realStart + 1].<br>                        ^true].<br>  <br>   lines := paragraph lines.<br>     startLine := paragraph lineIndexOfCharacterIndex: realStart.<br>          "start on a real line, not a wrapped line"<br>          [startLine = 1 or: [CharacterSet crlf includes: (paragraph string at: (lines at: startLine-1) last)]] whileFalse: [startLine := startLine - 1].<br>       stopLine := paragraph lineIndexOfCharacterIndex: (realStart max: realStop).<br>   start := (lines at: startLine) first.<br>         stop := (lines at: stopLine) last.<br>  <br>        "Pin the start of highlighting unless the selection starts a line"<br>          adjustStart := realStart > start.<br>  <br>      "Find the indentation of the least-indented non-blank line; never outdent more"<br>     "indentation := (startLine to: stopLine) inject: 1000 into:<br>              [:m :l |<br>              m min: (paragraph indentationOfLineIndex: l ifBlank: [:tabs | 1000])].<br>        indentation + delta <= 0 ifTrue: [^false]."<br>  <br>       numLines := stopLine + 1 - startLine.<br>         oldText := paragraph text copyFrom: start to: stop.<br>   newText := oldText species new: oldText size + ((numLines * delta) max: 0).<br>  <br>       "Do the actual work"<br>        newSize := 0.<br>         delta > 0<br>                  ifTrue: [| tabs |<br>                     tabs := oldText species new: delta withAll: Character tab.<br>                    oldText string lineIndicesDo: [:startL :endWithoutDelimiters :endL |<br>+                                 newText replaceFrom: 1 + newSize to: (newSize := newSize + delta) with: tabs startingAt: 1.<br>-                          startL < endWithoutDelimiters ifTrue: [newText replaceFrom: 1 + newSize to: (newSize := newSize + delta) with: tabs startingAt: 1].<br>                                newText replaceFrom: 1 + newSize to: (newSize := 1 + newSize + endL - startL) with: oldText startingAt: startL]]<br>              ifFalse: [| tab |<br>                     tab := Character tab.<br>                         oldText string lineIndicesDo: [:startL :endWithoutDelimiters :endL |<br>                                  | i |<br>                                 i := 0.<br>                               [i + delta < 0 and: [ i + startL <= endWithoutDelimiters and: [(oldText at: i + startL) == tab]]] whileTrue: [i := i + 1].<br>                              newText replaceFrom: 1 + newSize to: (newSize := 1 + newSize + endL - (i + startL)) with: oldText startingAt: i + startL]].<br>   newSize < newText size ifTrue: [newText := newText copyFrom: 1 to: newSize].<br>  <br>   "Adjust the range that will be highlighted later"<br>   adjustStart ifTrue: [realStart := (realStart + delta) max: start].<br>    realStop := realStop + newSize - oldText size.<br>  <br>    "Replace selection"<br>         self selectInvisiblyFrom: start to: stop.<br>     self replaceSelectionWith: newText.<br>   self selectFrom: realStart to: realStop.        "highlight only the original range"<br>         ^ true!<br><br><br></div></blockquote>
                                        </div></body>