Hi Christoph,
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.
Best, Marcle Am 07.03.2021 20:43:09 schrieb commits@source.squeak.org commits@source.squeak.org: Christoph Thiede uploaded a new version of Morphic to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1733.mcz
==================== Summary ====================
Name: Morphic-ct.1733 Author: ct Time: 7 March 2021, 8:42:45.180906 pm UUID: b8246d49-b132-40af-8ad4-7428c544390c Ancestors: Morphic-mt.1731
Fixes an eternity-old bug in TextEditor which skipped empty lines when indenting/outdenting the selection.
=============== Diff against Morphic-mt.1731 ===============
Item was changed: ----- Method: TextEditor>>inOutdent:delta: (in category 'editing keys') ----- inOutdent: aKeyboardEvent delta: delta "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"
| realStart realStop lines startLine stopLine start stop adjustStart "indentation" numLines oldText newText newSize | - "Operate on entire lines, but remember the real selection for re-highlighting later" realStart := self startIndex. realStop := self stopIndex - 1.
"Special case a caret on a line of its own, including weird case at end of paragraph" (realStart > realStop and: [realStart ifTrue: [delta ifTrue: [morph flash] ifFalse: [self replaceSelectionWith: Character tab asText. self selectAt: realStart + 1]. ^true].
lines := paragraph lines. startLine := paragraph lineIndexOfCharacterIndex: realStart. "start on a real line, not a wrapped line" [startLine = 1 or: [CharacterSet crlf includes: (paragraph string at: (lines at: startLine-1) last)]] whileFalse: [startLine := startLine - 1]. stopLine := paragraph lineIndexOfCharacterIndex: (realStart max: realStop). start := (lines at: startLine) first. stop := (lines at: stopLine) last.
"Pin the start of highlighting unless the selection starts a line" adjustStart := realStart > start.
"Find the indentation of the least-indented non-blank line; never outdent more" "indentation := (startLine to: stopLine) inject: 1000 into: [:m :l | m min: (paragraph indentationOfLineIndex: l ifBlank: [:tabs | 1000])]. indentation + delta newSize := 0. delta > 0 ifTrue: [| tabs | tabs := oldText species new: delta withAll: Character tab. oldText string lineIndicesDo: [:startL :endWithoutDelimiters :endL | + newText replaceFrom: 1 + newSize to: (newSize := newSize + delta) with: tabs startingAt: 1. - startL newText replaceFrom: 1 + newSize to: (newSize := 1 + newSize + endL - startL) with: oldText startingAt: startL]] ifFalse: [| tab | tab := Character tab. oldText string lineIndicesDo: [:startL :endWithoutDelimiters :endL | | i | i := 0. [i + delta newText replaceFrom: 1 + newSize to: (newSize := 1 + newSize + endL - (i + startL)) with: oldText startingAt: i + startL]]. newSize
"Adjust the range that will be highlighted later" adjustStart ifTrue: [realStart := (realStart + delta) max: start]. realStop := realStop + newSize - oldText size.
"Replace selection" self selectInvisiblyFrom: start to: stop. self replaceSelectionWith: newText. self selectFrom: realStart to: realStop. "highlight only the original range" ^ true!