handle edge cases in line-break removal function ('J' command). If the newline we find is the last character in the file, we don't delete it, because of Unix convention that text files always end with newlines.

This commit is contained in:
2025-09-06 10:05:06 +01:00
parent 764c5367eb
commit 8d241366a1

View File

@@ -100,34 +100,85 @@ struct
* 1J delettes 1 line break, 2J deletes 2, and so on. *)
fun helpRemoveLineBreaks (app, buffer, cursorIdx, count, time) =
if count = 0 then
finishAfterDeletingBuffer (app, cursorIdx, buffer, time, [])
(* we don't use Fn.initMsgs in this function.
* Removing line breaks is a discrete action which doesn't operate
* as a range the way that motions like 'dw' or 'diw'.
* Instead, a single character is deleted at different places.
* So it doesn't make any sense to use Fn.initMsgs
* which expects a range. *)
let
val buffer = LineGap.goToStart buffer
val searchString = #searchString app
val initialMsg = [SEARCH (buffer, searchString)]
in
finishAfterDeletingBuffer (app, cursorIdx, buffer, time, initialMsg)
end
else
let
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val newCursorIdx = Cursor.toNextChr (buffer, cursorIdx, #"\n")
val buffer = LineGap.delete (newCursorIdx, 1, buffer)
val buffer = LineGap.insert (newCursorIdx, " ", buffer)
val newCount = if newCursorIdx = cursorIdx then 0 else count - 1
in
helpRemoveLineBreaks (app, buffer, newCursorIdx, newCount, time)
if Cursor.isCursorAtStartOfLine (buffer, cursorIdx) then
(* if the cursor is at a linebreak, delete the linebreak
* and don't insert a space. *)
let val buffer = LineGap.delete (cursorIdx, 1, buffer)
in helpRemoveLineBreaks (app, buffer, cursorIdx, count - 1, time)
end
else
let
val newCursorIdx = Cursor.toNextChr (buffer, cursorIdx, #"\n")
val buffer = LineGap.goToIdx (newCursorIdx, buffer)
val clippedIdx = Cursor.clipIdx (buffer, newCursorIdx)
val buffer =
if clippedIdx = newCursorIdx then
let val buffer = LineGap.delete (newCursorIdx, 1, buffer)
in LineGap.insert (newCursorIdx, " ", buffer)
end
else
(* if clippedIdx and newCursorIdx are different,
* that means our previous search brought us past
* the ending #"\n" in the file.
* We don't want to delete the last newline in the file
* because of Unix convention
* so don't modify the buffer. *)
buffer
val newCount = if clippedIdx = cursorIdx then 0 else count - 1
in
helpRemoveLineBreaks (app, buffer, clippedIdx, newCount, time)
end
end
fun removeLineBreaks (app: app_type, count, time) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val newCursorIdx = Cursor.toNextChr (buffer, cursorIdx, #"\n")
in
if cursorIdx = newCursorIdx then
(* no change *)
NormalFinish.clearMode app
if Cursor.isCursorAtStartOfLine (buffer, cursorIdx) then
let val buffer = LineGap.delete (cursorIdx, 1, buffer)
in helpRemoveLineBreaks (app, buffer, cursorIdx, count - 1, time)
end
else
let
val buffer = LineGap.delete (newCursorIdx, 1, buffer)
val buffer = LineGap.insert (newCursorIdx, " ", buffer)
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val newCursorIdx = Cursor.toNextChr (buffer, cursorIdx, #"\n")
val buffer = LineGap.goToIdx (newCursorIdx, buffer)
val clippedIdx = Cursor.clipIdx (buffer, newCursorIdx)
in
helpRemoveLineBreaks (app, buffer, newCursorIdx, count - 1, time)
if cursorIdx = newCursorIdx orelse newCursorIdx <> clippedIdx then
(* no change, either because no #"\n" was found
* or because the next #"\n" found is the last character in the
* file.
* We don't delete the last #"\n" in the file
* because Unix convention is that text files always
* end with newlines. *)
NormalFinish.clearMode app
else
let
val buffer = LineGap.delete (newCursorIdx, 1, buffer)
val buffer = LineGap.insert (newCursorIdx, " ", buffer)
in
helpRemoveLineBreaks (app, buffer, newCursorIdx, count - 1, time)
end
end
end