Files
sml-projects/shf/fcore/normal-mode/normal-yank.sml
Humza Shahid 6b91d64fc3 Add 'shf/' from commit 'b6c5a95b664aeb861d7b33ffc9eefe447ba99dd7'
git-subtree-dir: shf
git-subtree-mainline: 401408448f
git-subtree-split: b6c5a95b66
2026-04-24 00:27:49 +01:00

535 lines
16 KiB
Standard ML

structure NormalYank =
struct
open AppType
open DrawMsg
open MailboxType
fun finish (app, buffer, yankedString) =
let
val msgs = [DRAW (YANK yankedString)]
val mode = NORMAL_MODE ""
in
NormalModeWith.modeAndBuffer (app, buffer, mode, msgs)
end
fun yankLeft (app: app_type, count) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val min = Cursor.vi0 (buffer, cursorIdx)
val low = Cursor.viH (buffer, cursorIdx, count)
val low = Int.max (min, low)
val length = cursorIdx - low
val str = LineGap.substring (low, length, buffer)
in
finish (app, buffer, str)
end
fun yankRight (app: app_type, count) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val endOfLineIdx = Cursor.viDlr (buffer, cursorIdx, 1) + 1
val high = Cursor.viL (buffer, cursorIdx, count)
val high = Int.min (high, endOfLineIdx)
val length = high - cursorIdx
val buffer = LineGap.goToIdx (high, buffer)
val str = LineGap.substring (cursorIdx, length, buffer)
in
finish (app, buffer, str)
end
fun yankLineUp (app: app_type, count) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorLineNumber =
if Cursor.isNextChrEndOfLine (buffer, cursorIdx) then
LineGap.idxToLineNumber (cursorIdx + 1, buffer)
else
LineGap.idxToLineNumber (cursorIdx, buffer)
val newCursorLineNumber = Int.max (cursorLineNumber - count, 0)
in
if cursorLineNumber = 0 then
NormalFinish.clearMode app
else if newCursorLineNumber = 0 then
let
val endOfLine = Cursor.viDlr (buffer, cursorIdx, 1)
val buffer = LineGap.goToIdx (endOfLine, buffer)
val endOfLine =
if Cursor.isCursorAtStartOfLine (buffer, endOfLine) then
endOfLine + 1
else
endOfLine + 2
val buffer = LineGap.goToIdx (endOfLine, buffer)
val str = LineGap.substring (0, endOfLine, buffer)
in
finish (app, buffer, str)
end
else
let
val endOfLine = Cursor.viDlr (buffer, cursorIdx, 1)
val buffer = LineGap.goToIdx (endOfLine, buffer)
val endsOnNewline = Cursor.isCursorAtStartOfLine (buffer, endOfLine)
val endOfLine = if endsOnNewline then endOfLine else endOfLine + 1
val newCursorLineNumber =
if endsOnNewline andalso endOfLine = #textLength buffer - 1 then
newCursorLineNumber - 1
else
newCursorLineNumber
val buffer = LineGap.goToLine (newCursorLineNumber, buffer)
val lineIdx = LineGap.lineNumberToIdx (newCursorLineNumber, buffer)
val length = endOfLine - lineIdx
val buffer = LineGap.goToIdx (endOfLine, buffer)
val str = LineGap.substring (lineIdx + 1, length, buffer)
in
finish (app, buffer, str)
end
end
fun yankLineDown (app: app_type, count) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val startIdx = Cursor.vi0 (buffer, cursorIdx)
val buffer = LineGap.goToIdx (startIdx, buffer)
val startLine =
if Cursor.isCursorAtStartOfLine (buffer, startIdx) then
LineGap.idxToLineNumber (startIdx, buffer)
else
LineGap.idxToLineNumber (startIdx + 1, buffer)
val endLine = startLine + count + 1
val buffer = LineGap.goToLine (endLine, buffer)
val endLineIdx = LineGap.lineNumberToIdx (endLine, buffer)
val buffer = LineGap.goToIdx (endLineIdx - 1, buffer)
(* get "real" endLine by not considering newline after non-newline *)
val endLine =
if Cursor.isOnNewlineAfterChr (buffer, endLineIdx - 1) then
LineGap.idxToLineNumber (endLineIdx - 1, buffer)
else
LineGap.idxToLineNumber (endLineIdx, buffer)
in
if endLineIdx = #textLength buffer andalso endLine = startLine then
(* cursor is already on last line so don't yank *)
NormalFinish.clearMode app
else
let
val endLineIdx = endLineIdx + 1
val length = endLineIdx - startIdx
(* perform the actual yank *)
val buffer = LineGap.goToIdx (endLineIdx, buffer)
val str = LineGap.substring (startIdx, length, buffer)
in
finish (app, buffer, str)
end
end
fun yankLine (app: app_type, count) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val low = Cursor.vi0 (buffer, cursorIdx)
val buffer = LineGap.goToIdx (low, buffer)
val high = Cursor.viDlrForDelete (buffer, low, count)
val buffer = LineGap.goToIdx (high, buffer)
val length = high - low
val str = LineGap.substring (low, length, buffer)
in
finish (app, buffer, str)
end
fun yankToStartOfLine (app: app_type) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val low = Cursor.vi0 (buffer, cursorIdx)
val length = cursorIdx - low
val str = LineGap.substring (low, length, buffer)
in
finish (app, buffer, str)
end
fun yankWhenMovingBack (app: app_type, fMove, count) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val low = fMove (buffer, cursorIdx, count)
val length = cursorIdx - low
val str = LineGap.substring (low, length, buffer)
in
finish (app, buffer, str)
end
fun yankWhenMovingBackPlusOne (app: app_type, fMove, count) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val low = fMove (buffer, cursorIdx, count)
val length = (cursorIdx + 1) - low
val str = LineGap.substring (low, length, buffer)
in
finish (app, buffer, str)
end
fun yankWhenMovingForward (app: app_type, fMove, count) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val high = fMove (buffer, cursorIdx, count)
val beforeHigh = high - 1
val buffer = LineGap.goToIdx (beforeHigh, buffer)
val high =
if Cursor.isOnNewlineAfterChr (buffer, beforeHigh) then beforeHigh
else high
val length = high - cursorIdx
val str = LineGap.substring (cursorIdx, length, buffer)
in
finish (app, buffer, str)
end
fun yankToFirstNonSpaceChr (app: app_type) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val otherIdx = Cursor.vi0 (buffer, cursorIdx)
val buffer = LineGap.goToIdx (otherIdx, buffer)
val otherIdx = Cursor.firstNonSpaceChr (buffer, otherIdx)
in
if cursorIdx > otherIdx then
(* yanking backwards from cursorIdx *)
let
val length = cursorIdx - otherIdx + 1
val buffer = LineGap.goToIdx (otherIdx, buffer)
val str = LineGap.substring (otherIdx, length, buffer)
in
finish (app, buffer, str)
end
else if cursorIdx < otherIdx then
(* yanking forward from cursorIdx *)
let
val length = otherIdx - cursorIdx
val str = LineGap.substring (cursorIdx, length, buffer)
in
finish (app, buffer, str)
end
else
NormalFinish.clearMode app
end
fun yankToEndOfText (app: app_type) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToEnd buffer
val {rightStrings, idx, ...} = buffer
val finishIdx = Int.max (0, idx - 1)
val length = finishIdx - cursorIdx
val str = LineGap.substring (cursorIdx, length, buffer)
in
finish (app, buffer, str)
end
fun yankToMatchingPair (app: app_type) =
let
val {buffer, cursorIdx, ...} = app
val otherIdx = Cursor.matchPair (buffer, cursorIdx)
in
if cursorIdx = otherIdx then
NormalFinish.clearMode app
else
let
val low = Int.min (cursorIdx, otherIdx)
val high = Int.max (cursorIdx, otherIdx)
val length = high - low + 1
val buffer = LineGap.goToIdx (high, buffer)
val str = LineGap.substring (low, length, buffer)
in
finish (app, buffer, str)
end
end
fun yankToNextMatch (app: app_type, count) =
let
val {cursorIdx, searchList, buffer, ...} = app
val high = PersistentVector.nextMatch (cursorIdx, searchList, count)
in
if high = ~1 orelse high <= cursorIdx then
NormalFinish.clearMode app
else
let
val length = high - cursorIdx
val buffer = LineGap.goToIdx (high, buffer)
val str = LineGap.substring (cursorIdx, length, buffer)
in
finish (app, buffer, str)
end
end
fun yankToPrevMatch (app: app_type, count) =
let
val {cursorIdx, searchList, buffer, ...} = app
val low = PersistentVector.prevMatch (cursorIdx, searchList, count)
in
if low = ~1 orelse low >= cursorIdx then
NormalFinish.clearMode app
else
let
val length = cursorIdx - low
val str = LineGap.substring (low, length, buffer)
in
finish (app, buffer, str)
end
end
fun helpYankToChr
(app: app_type, buffer, cursorIdx, otherIdx, count, fMove, fInc, chr) =
if count = 0 then
let
val low = Int.min (cursorIdx, otherIdx)
val high = Int.max (cursorIdx, otherIdx)
val length = high - low
val buffer = LineGap.goToIdx (high, buffer)
val str = LineGap.substring (low, length, buffer)
in
finish (app, buffer, str)
end
else
let
val buffer = LineGap.goToIdx (otherIdx, buffer)
val newOtherIdx = fMove (buffer, otherIdx, chr)
val newCount = if newOtherIdx = otherIdx then 0 else count - 1
val newOtherIdx = fInc (newOtherIdx, 1)
in
helpYankToChr
(app, buffer, cursorIdx, newOtherIdx, newCount, fMove, fInc, chr)
end
fun yankToNextChr (app: app_type, count, chr) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val newCursorIdx =
Cursor.toNextChr (buffer, cursorIdx, {findChr = chr, count = count})
in
if newCursorIdx = ~1 then
NormalFinish.clearMode app
else
let
val length = newCursorIdx - cursorIdx + 1
val buffer = LineGap.goToIdx (newCursorIdx, buffer)
val str = LineGap.substring (cursorIdx, length, buffer)
in
finish (app, buffer, str)
end
end
fun yankTillNextChr (app: app_type, count, chr) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val newCursorIdx =
Cursor.toNextChr (buffer, cursorIdx, {findChr = chr, count = count})
in
if newCursorIdx = ~1 then
NormalFinish.clearMode app
else
let
val length = newCursorIdx - cursorIdx
val buffer = LineGap.goToIdx (newCursorIdx, buffer)
val str = LineGap.substring (cursorIdx, length, buffer)
in
finish (app, buffer, str)
end
end
fun yankToPrevChr (app: app_type, count, chr) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val newCursorIdx =
Cursor.toPrevChr (buffer, cursorIdx, {findChr = chr, count = count})
in
if newCursorIdx = ~1 then
NormalFinish.clearMode app
else
let
val length = cursorIdx - newCursorIdx
val str = LineGap.substring (newCursorIdx, length, buffer)
in
finish (app, buffer, str)
end
end
fun yankTillPrevChr (app: app_type, count, chr) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val newCursorIdx =
Cursor.toPrevChr (buffer, cursorIdx, {findChr = chr, count = count})
in
if newCursorIdx = ~1 then
NormalFinish.clearMode app
else
let
val newCursorIdx = newCursorIdx + 1
val length = cursorIdx - newCursorIdx
val str = LineGap.substring (newCursorIdx, length, buffer)
in
finish (app, buffer, str)
end
end
fun yankToStart (app: app_type) =
let
val {cursorIdx, buffer, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val high = Cursor.viDlrForDelete (buffer, cursorIdx, 1)
val buffer = LineGap.goToIdx (high, buffer)
val str = LineGap.substring (0, high, buffer)
in
finish (app, buffer, str)
end
fun yankInsideWord (app: app_type) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val low = Cursor.prevWordStrict (buffer, cursorIdx, 1)
val high = Cursor.endOfWordStrict (buffer, cursorIdx, 1)
val high = high + 1
val buffer = LineGap.goToIdx (high, buffer)
val length = high - low
val str = LineGap.substring (low, length, buffer)
in
if str = "\n" then NormalFinish.clearMode app
else finish (app, buffer, str)
end
fun yankInsideWORD (app: app_type) =
let
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val low = Cursor.prevWORDStrict (buffer, cursorIdx, 1)
val high = Cursor.endOfWORDStrict (buffer, cursorIdx, 1)
val high = high + 1
val buffer = LineGap.goToIdx (high, buffer)
val length = high - low
val str = LineGap.substring (low, length, buffer)
in
if str = "\n" then NormalFinish.clearMode app
else finish (app, buffer, str)
end
fun finishAfterYankInside (app: app_type, low, high, buffer) =
let
val length = high - low
val str = LineGap.substring (low, length, buffer)
in
finish (app, buffer, str)
end
fun yankInsideChrOpen (app: app_type, chr) =
let
val {cursorIdx, buffer, ...} = app
val start = cursorIdx + 1
val buffer = LineGap.goToIdx (start, buffer)
val low = Cursor.toPrevChr (buffer, start, {findChr = chr, count = 1})
val buffer = LineGap.goToIdx (low, buffer)
val high = Cursor.matchPair (buffer, low)
val buffer = LineGap.goToIdx (high, buffer)
val low = low + 1
in
if low = high then NormalFinish.clearMode app
else finishAfterYankInside (app, low, high, buffer)
end
fun yankInsideChrClose (app: app_type, chr) =
let
val {cursorIdx, buffer, ...} = app
val start = Int.max (cursorIdx - 1, 0)
val buffer = LineGap.goToIdx (start, buffer)
val high = Cursor.toNextChr (buffer, start, {findChr = chr, count = 1})
val buffer = LineGap.goToIdx (high, buffer)
val low = Cursor.matchPair (buffer, high) + 1
in
if low = high then NormalFinish.clearMode app
else finishAfterYankInside (app, low, high, buffer)
end
fun yankAroundChrOpen (app: app_type, chr) =
let
val {cursorIdx, buffer, ...} = app
val start = cursorIdx + 1
val buffer = LineGap.goToIdx (start, buffer)
val low = Cursor.toPrevChr (buffer, start, {findChr = chr, count = 1})
val buffer = LineGap.goToIdx (low, buffer)
val high = Cursor.matchPair (buffer, low) + 1
val buffer = LineGap.goToIdx (high, buffer)
val low = low
in
if low = high then NormalFinish.clearMode app
else finishAfterYankInside (app, low, high, buffer)
end
fun yankAroundChrClose (app: app_type, chr) =
let
val {cursorIdx, buffer, ...} = app
val start = Int.max (cursorIdx - 1, 0)
val buffer = LineGap.goToIdx (start, buffer)
val high = Cursor.toNextChr (buffer, start, {findChr = chr, count = 1})
val buffer = LineGap.goToIdx (high, buffer)
val low = Cursor.matchPair (buffer, high)
val high = high + 1
in
if low = high then NormalFinish.clearMode app
else finishAfterYankInside (app, low, high, buffer)
end
end